PageRenderTime 61ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/system/expressionengine/third_party/ce_cache/mod.ce_cache.php

https://gitlab.com/sops21/mt-rubidoux
PHP | 1361 lines | 836 code | 200 blank | 325 comment | 214 complexity | 4dbe4488a66a8582f88f085eb650f277 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * CE Cache - Module file.
  4. *
  5. * @author Aaron Waldon
  6. * @copyright Copyright (c) 2013 Causing Effect
  7. * @license http://www.causingeffect.com/software/expressionengine/ce-cache/license-agreement
  8. * @link http://www.causingeffect.com
  9. */
  10. class Ce_cache
  11. {
  12. //a reference to the instantiated class factory
  13. private $drivers;
  14. //debug mode flag
  15. private $debug = false;
  16. //the relative directory path to be appended to the cache path
  17. private $id_prefix = '';
  18. //a flag to indicate whether or not the cache is setup
  19. public $is_cache_setup = false;
  20. //this will hold the actual URL, or the URL specified by the user
  21. private $cache_url = '';
  22. /**
  23. * Constructor
  24. */
  25. public function __construct()
  26. {
  27. $this->EE = get_instance();
  28. //if the template debugger is enabled, and a super admin user is logged in, enable debug mode
  29. $this->debug = false;
  30. if ( $this->EE->session->userdata['group_id'] == 1 && $this->EE->config->item('template_debugging') == 'y' )
  31. {
  32. $this->debug = true;
  33. }
  34. //include CE Cache Utilities
  35. if ( ! class_exists( 'Ce_cache_utils' ) )
  36. {
  37. include PATH_THIRD . 'ce_cache/libraries/Ce_cache_utils.php';
  38. }
  39. }
  40. /**
  41. * This method will check if the cache id exists, and return it if it does. If the cache id does not exists, it will cache the data and return it.
  42. * @return string
  43. */
  44. public function it()
  45. {
  46. //setup the cache drivers if needed
  47. $this->setup_cache();
  48. //get the tagdata
  49. $tagdata = $this->no_results_tagdata();
  50. //get the id
  51. if ( false === $id = $this->fetch_id( __METHOD__ ) )
  52. {
  53. return $tagdata;
  54. }
  55. //check the cache for the id
  56. $item = $this->get( true );
  57. if ( $item === false ) //the item could not be found for any of the drivers
  58. {
  59. //specify that we want the save method to return the content
  60. $this->EE->TMPL->tagparams['show'] = 'yes';
  61. //attempt to save the content
  62. return $this->save();
  63. }
  64. //the item was found, parse the item and return it
  65. return $this->process_return_data( $item );
  66. }
  67. /**
  68. * Save an item to the cache.
  69. * @return string
  70. */
  71. public function save()
  72. {
  73. //setup the cache drivers if needed
  74. $this->setup_cache();
  75. //did the user elect to ignore this tag?
  76. if ( strtolower( $this->EE->TMPL->fetch_param( 'ignore_if_dummy' ) ) == 'yes' && $this->drivers[0]->name() == 'dummy' ) //ignore this entire tag if the dummy driver is being used
  77. {
  78. return $this->EE->TMPL->no_results();
  79. }
  80. //don't process googlebot save requests, as it can caused problems by hitting an insane number of non-existant URLs
  81. //note: we do this here, because we still want to return pages quickly to google bot if they are already cached, we just don't want to save pages it requests
  82. if ( $this->EE->config->item( 'ce_cache_block_bots' ) != 'no' && $this->is_bot() )
  83. {
  84. $this->drivers = Ce_cache_factory::factory( array( 'dummy' ) );
  85. $this->is_cache_setup = false;
  86. }
  87. //get the tagdata
  88. $tagdata = $this->no_results_tagdata();
  89. //trim the tagdata?
  90. $should_trim = strtolower( $this->determine_setting( 'trim', 'no' ) );
  91. $should_trim = ( $should_trim == 'yes' || $should_trim == 'y' || $should_trim == 'on' );
  92. //trim here in case the data needs to be returned early
  93. if ( $should_trim )
  94. {
  95. $tagdata = trim( $tagdata );
  96. }
  97. //get the id
  98. if ( false === $id = $this->fetch_id( __METHOD__ ) )
  99. {
  100. return $tagdata;
  101. }
  102. //get the time to live (defaults to 60 minutes)
  103. $ttl = (int) $this->determine_setting( 'seconds', '3600' );
  104. //save the previous tags
  105. $previous_tags = isset( $this->EE->session->cache[ 'Ce_cache' ]['tags'] ) ? $this->EE->session->cache[ 'Ce_cache' ]['tags'] : '';
  106. //clear the current tags
  107. $this->EE->session->cache[ 'Ce_cache' ]['tags'] = '';
  108. //flag that caching is happening--important for escaped content
  109. $this->EE->session->cache[ 'Ce_cache' ]['is_caching'] = true;
  110. //do we need to process the data?
  111. if ( $this->EE->TMPL->fetch_param( 'process' ) != 'no' ) //we need to process the data
  112. {
  113. //we're going to escape the logged_in and logged_out conditionals, since the Channel Entries loop adds them as variables.
  114. $tagdata = str_replace( array( 'logged_in', 'logged_out' ), array( 'ce_cache-in_logged', 'ce_cache-out_logged' ), $tagdata );
  115. //pre parse hook
  116. if ($this->EE->extensions->active_hook('ce_cache_pre_parse'))
  117. {
  118. $tagdata = $this->EE->extensions->call('ce_cache_pre_parse', $tagdata);
  119. }
  120. //parse the data
  121. $tagdata = $this->parse_as_template( $tagdata );
  122. //post parse hook
  123. if ($this->EE->extensions->active_hook('ce_cache_post_parse'))
  124. {
  125. $tagdata = $this->EE->extensions->call('ce_cache_post_parse', $tagdata);
  126. }
  127. }
  128. $tagdata = $this->unescape_tagdata( $tagdata );
  129. //make sure the template debugger is not getting cached, as that is bad
  130. $debugger_pos = strpos( $tagdata, '<div style="color: #333; background-color: #ededed; margin:10px; padding-bottom:10px;"><div style="text-align: left; font-family: Sans-serif; font-size: 11px; margin: 12px; padding: 6px"><hr size=\'1\'><b>TEMPLATE DEBUGGING</b><hr' );
  131. if ( $debugger_pos !== false )
  132. {
  133. $tagdata = substr_replace( $tagdata, '', $debugger_pos, -1 );
  134. }
  135. //pre save hook
  136. if ($this->EE->extensions->active_hook('ce_cache_pre_save'))
  137. {
  138. $tagdata = $this->EE->extensions->call('ce_cache_pre_save', $tagdata, 'fragment');
  139. }
  140. //trim again since the data may be much different now
  141. if ( $should_trim )
  142. {
  143. $tagdata = trim( $tagdata );
  144. }
  145. //loop through the drivers and try to save the data
  146. foreach ( $this->drivers as $driver )
  147. {
  148. if ( $driver->set( $id, $tagdata, $ttl ) === false ) //save unsuccessful
  149. {
  150. $this->log_debug_message( __METHOD__, "Something went wrong and the data for '{$id}' was not cached using the " . $driver->name() . " driver." );
  151. }
  152. else //save successful
  153. {
  154. $this->log_debug_message( __METHOD__, "The data for '{$id}' was successfully cached using the " . $driver->name() . " driver." );
  155. //if we are saving the item for the first time, we are going to keep track of the drivers and ids, so we can clear the cached items later if this ends up being a 404 page
  156. if ( $driver->name() != 'dummy' )
  157. {
  158. $this->EE->session->cache[ 'Ce_cache' ]['cached_items'][] = array( 'driver' => $driver->name(), 'id' => $id );
  159. $this->register_the_shutdown();
  160. }
  161. break;
  162. }
  163. }
  164. //flag that caching is finished--important for escaped content
  165. $this->EE->session->cache[ 'Ce_cache' ]['is_caching'] = false;
  166. //save the tags, if applicable
  167. if ( $this->drivers[0]->name() != 'dummy' )
  168. {
  169. $this->save_tags( $id, $this->EE->TMPL->fetch_param( 'tags' ) . $this->EE->session->cache[ 'Ce_cache' ]['tags'] );
  170. }
  171. //add the new tags to the previous tags, so they can be used for the static driver
  172. $this->EE->session->cache[ 'Ce_cache' ]['tags'] = $previous_tags . $this->EE->session->cache[ 'Ce_cache' ]['tags'];
  173. if ( $this->EE->TMPL->fetch_param( 'show' ) == 'yes' )
  174. {
  175. //parse any segment variables
  176. return $this->parse_vars( $tagdata );
  177. }
  178. unset( $tagdata );
  179. return '';
  180. }
  181. /**
  182. * Save the static page.
  183. */
  184. public function stat()
  185. {
  186. //is the user logged in?
  187. $logged_in = ($this->EE->session->userdata['member_id'] != 0);
  188. //see if there is a reason to prevent caching the page
  189. if (
  190. ( isset( $this->EE->session->cache['ep_better_workflow']['is_preview'] ) && $this->EE->session->cache['ep_better_workflow']['is_preview'] === true ) //better workflow draft
  191. || ( isset( $_GET['bwf_dp'] ) && $_GET['bwf_dp'] == 't' ) //another bwf check (from Matt Green)
  192. || ( isset( $_GET['publisher_status'] ) && $_GET['publisher_status'] == 'draft' ) // publisher check (from Fusionary)
  193. || $this->ee_string_to_bool( $this->determine_setting( 'off', 'no' ) ) //ce cache is off
  194. || ( $this->EE->config->item( 'ce_cache_block_bots' ) != 'no' && $this->is_bot() ) //bot page
  195. || ($this->ee_string_to_bool( $this->determine_setting( 'logged_in_only', 'no', 'static' ) ) && ! $logged_in ) //logged in only, but not logged in
  196. || ($this->ee_string_to_bool( $this->determine_setting( 'logged_out_only', 'no', 'static' ) ) && $logged_in ) //logged out only, but is logged in
  197. || ( ! empty( $_POST ) && $this->ee_string_to_bool( $this->determine_setting( 'ignore_post_requests', 'yes' ) ) && $_POST != array( 'entry_id' => '' ) ) //a POST page and ignore_post_requests is set to "yes"
  198. ) //no caching
  199. {
  200. return;
  201. }
  202. if ( ! isset( $this->EE->session->cache[ 'Ce_cache' ][ 'static' ] ) )
  203. {
  204. //make sure we set the cache_url for the path
  205. $this->determine_cache_url();
  206. //get the time to live (defaults to 60 minutes)
  207. $this->EE->session->cache[ 'Ce_cache' ][ 'static' ] = array(
  208. 'seconds' => (int) $this->determine_setting( 'seconds', '3600' ),
  209. 'tags' => $this->EE->TMPL->fetch_param( 'tags' )
  210. );
  211. }
  212. $this->register_the_shutdown();
  213. }
  214. private function register_the_shutdown()
  215. {
  216. //register the shutdown function if needed
  217. if ( empty( $this->EE->session->cache[ 'Ce_cache' ][ 'shutdown_is_registered' ] ) )
  218. {
  219. $this->EE->session->cache[ 'Ce_cache' ][ 'shutdown_is_registered' ] = true;
  220. //register the shutdown function
  221. register_shutdown_function( array( $this, 'shut_it_down' ) );
  222. }
  223. }
  224. /**
  225. * Escapes the passed-in content so that it will not be parsed before being cached.
  226. * @return string
  227. */
  228. public function escape()
  229. {
  230. $tagdata = false;
  231. //if there is pre_escaped tagdata, use it
  232. $tag_parts = $this->EE->TMPL->tagparts;
  233. if ( is_array( $tag_parts ) && isset( $tag_parts[2] ) )
  234. {
  235. if ( isset( $this->EE->session->cache[ 'Ce_cache' ]['pre_escape'][ 'id_' . $tag_parts[2] ] ) )
  236. {
  237. $tagdata = $this->EE->session->cache[ 'Ce_cache' ]['pre_escape'][ 'id_' . $tag_parts[2] ];
  238. }
  239. }
  240. if ( $tagdata === false ) //there was no pre-escaped tagdata, get the no_results tagdata
  241. {
  242. $tagdata = $this->no_results_tagdata();
  243. }
  244. if ( trim( $tagdata ) == '' ) //there is no tagdata
  245. {
  246. return $tagdata;
  247. }
  248. else if ( empty( $this->EE->session->cache[ 'Ce_cache' ]['is_caching'] ) ) //we're not inside of a tagdata loop
  249. {
  250. return $this->parse_vars( $tagdata );
  251. }
  252. //create a 16 character placeholder
  253. $placeholder = '-ce_cache_placeholder:' . hash( 'md5', $tagdata );// . '_' . mt_rand( 0, 1000000 );
  254. //add to the cache
  255. $this->EE->session->cache[ 'Ce_cache' ]['placeholder-keys'][] = $placeholder;
  256. $this->EE->session->cache[ 'Ce_cache' ]['placeholder-values'][] = $tagdata;
  257. //return the placeholder
  258. return $placeholder;
  259. }
  260. /**
  261. * Add one or more tags.
  262. *
  263. * @return string
  264. */
  265. public function add_tags()
  266. {
  267. //get the tagdata
  268. $tagdata = trim( $this->EE->TMPL->tagdata );
  269. if ( empty( $tagdata ) )
  270. {
  271. return $this->EE->TMPL->no_results();
  272. }
  273. //make sure the tags session cache exists
  274. if ( empty( $this->EE->session->cache[ 'Ce_cache' ]['tags'] ) )
  275. {
  276. $this->EE->session->cache[ 'Ce_cache' ]['tags'] = '';
  277. }
  278. //turn the tagdata into cleaned tags
  279. $tagdata = strtolower( trim( $tagdata ) );
  280. $tags = explode( '|', $tagdata );
  281. foreach ( $tags as $index => $tag )
  282. {
  283. $tag = trim( $tag );
  284. if ( empty( $tag ) ) //remove empty tag
  285. {
  286. unset( $tags[ $index ] );
  287. }
  288. else //add the cleaned up tag
  289. {
  290. $tags[ $index ] = $tag;
  291. }
  292. }
  293. $tags = implode( '|', $tags );
  294. //add the tags
  295. $this->log_debug_message( __METHOD__, 'The following tags were added: ' . $tags );
  296. $this->EE->session->cache[ 'Ce_cache' ]['tags'] .= '|' . $tags;
  297. //return an empty string
  298. return '';
  299. }
  300. /**
  301. * Alias for the add_tags method.
  302. *
  303. * @return string
  304. */
  305. public function add_tag()
  306. {
  307. return $this->add_tags();
  308. }
  309. /**
  310. * Returns whether or not a driver is supported.
  311. * @return int
  312. */
  313. public function is_supported()
  314. {
  315. //get the driver
  316. $driver = $this->EE->TMPL->fetch_param( 'driver' );
  317. //load the class if needed
  318. $this->include_factory();
  319. //see if the driver is supported
  320. return ( Ce_cache_factory::is_supported( $driver ) ) ? 1 : 0;
  321. }
  322. /**
  323. * Get an item from the cache.
  324. * @param bool $internal_request Was this method requested from this class (true) or from the template (false).
  325. * @return bool|int
  326. */
  327. public function get( $internal_request = false )
  328. {
  329. //setup the cache drivers if needed
  330. $this->setup_cache();
  331. //get the id
  332. if ( false === $id = $this->fetch_id( __METHOD__ ) )
  333. {
  334. return $this->EE->TMPL->no_results();
  335. }
  336. //loop through the drivers and attempt to find the cache item
  337. foreach ( $this->drivers as $driver )
  338. {
  339. $item = $driver->get( $id );
  340. if ( $item !== false ) //we found the item
  341. {
  342. $this->log_debug_message( __METHOD__, "The '{$id}' item was found for the " . $driver->name() . " driver." );
  343. //process the data and return it
  344. return $this->process_return_data( $item );
  345. }
  346. }
  347. //the item was not found in the cache of any of the drivers
  348. return ( $internal_request ) ? false : $this->EE->TMPL->no_results();
  349. }
  350. /**
  351. * Delete something from the cache.
  352. * @return string|void
  353. */
  354. public function delete()
  355. {
  356. //setup the cache drivers if needed
  357. $this->setup_cache();
  358. //get the id
  359. if ( false === $id = $this->fetch_id( __METHOD__ ) )
  360. {
  361. return $this->EE->TMPL->no_results();
  362. }
  363. //loop through the drivers and attempt to delete the cache item for each one
  364. foreach ( $this->drivers as $driver )
  365. {
  366. if ( $driver->delete( $id ) !== false )
  367. {
  368. $this->log_debug_message( __METHOD__, "The '{$id}' item was deleted for the " . $driver->name() . " driver." );
  369. }
  370. }
  371. //delete all of the current tags for this item
  372. $this->EE->db->query( 'DELETE FROM exp_ce_cache_tagged_items WHERE item_id = ?', array( $id ) );
  373. }
  374. /**
  375. * Manually clears items and/or tags, and optionally refreshes the cleared items.
  376. * @return void
  377. */
  378. public function clear()
  379. {
  380. //get the items
  381. $items = $this->EE->TMPL->fetch_param( 'items' );
  382. $items = empty( $items ) ? array() : explode( '|', $this->reduce_pipes( $items, false ) );
  383. //get the tags
  384. $tags = $this->EE->TMPL->fetch_param( 'tags' );
  385. $tags = empty( $tags ) ? array() : explode( '|', $this->reduce_pipes( $tags ) );
  386. //do we need to continue?
  387. if ( empty( $items ) && empty( $tags ) ) //we don't have any items or tags
  388. {
  389. return;
  390. }
  391. //refresh?
  392. $refresh = $this->EE->TMPL->fetch_param( 'refresh' );
  393. $refresh_time = 1;
  394. if ( is_numeric( $refresh ) )
  395. {
  396. $refresh_time = round( $refresh );
  397. $refresh = true;
  398. }
  399. else
  400. {
  401. $refresh = false;
  402. }
  403. //load the cache break class, if needed
  404. if ( ! class_exists( 'Ce_cache_break' ) )
  405. {
  406. include PATH_THIRD . 'ce_cache/libraries/Ce_cache_break.php';
  407. }
  408. //instantiate the class break and call the break cache method
  409. $cache_break = new Ce_cache_break();
  410. $cache_break->break_cache( $items, $tags, $refresh, $refresh_time );
  411. }
  412. /**
  413. * Get information about a cached item.
  414. *
  415. * @return string
  416. */
  417. public function get_metadata()
  418. {
  419. //setup the cache drivers if needed
  420. $this->setup_cache();
  421. //get the id
  422. if ( false === $id = $this->fetch_id( __METHOD__ ) )
  423. {
  424. return $this->EE->TMPL->no_results();
  425. }
  426. //the array of meta data items
  427. $item = array();
  428. //loop through the drivers and attempt to find the cache item
  429. foreach ( $this->drivers as $driver )
  430. {
  431. //get the info
  432. if( !! $info = $driver->meta( $id, false ) )
  433. {
  434. //info contains the keys: 'expiry', 'made', 'ttl', 'ttl_remaining', and 'content'
  435. //add in legacy keys
  436. $info['expire'] = $info['expiry'];
  437. $info['mtime'] = $info['made'];
  438. //add in driver key
  439. $info['driver'] = $driver->name();
  440. $item = $info;
  441. break;
  442. }
  443. }
  444. //make sure we have at least one result
  445. if ( count( $item ) == 0 )
  446. {
  447. return $this->EE->TMPL->no_results();
  448. }
  449. //get the tagdata
  450. $tagdata = $this->no_results_tagdata();
  451. //parse the conditionals
  452. $tagdata = $this->EE->functions->prep_conditionals( $tagdata, $item );
  453. //return the parsed tagdata
  454. return $this->EE->TMPL->parse_variables_row( $tagdata, $item );
  455. }
  456. /**
  457. * Purges the cache.
  458. * @return void
  459. */
  460. public function clean()
  461. {
  462. $site_only = trim( $this->EE->TMPL->fetch_param( 'site_only', 'yes' ) );
  463. $force = $this->ee_string_to_bool( trim( $this->EE->TMPL->fetch_param( 'force', 'yes' ) ) );
  464. //get the driver classes
  465. $drivers = $this->get_drivers_array( true, $force );
  466. $this->include_factory();
  467. $this->drivers = Ce_cache_factory::factory( $drivers, true );
  468. //loop through the drivers and purge their respective caches
  469. foreach ( $this->drivers as $driver )
  470. {
  471. if ( $this->ee_string_to_bool( $site_only ) )
  472. {
  473. //get the site name
  474. $site = Ce_cache_utils::get_site_label();
  475. $site = 'ce_cache/' . $site;
  476. $site = rtrim( $site ) . '/'; //make sure there is a trailing slash for this to work
  477. //attempt to get the items for the path
  478. if ( false === $items = $driver->get_all( $site ) )
  479. {
  480. $this->log_debug_message( __METHOD__, "No items were found for the current site cache for the " . $driver->name() . " driver." );
  481. return;
  482. }
  483. //we've got items
  484. foreach ( $items as $item )
  485. {
  486. if ( $driver->delete( $site . ( ( $driver == 'db' || $driver == 'apc' ) ? $item['id'] : $item ) ) === false )
  487. {
  488. $this->log_debug_message( __METHOD__, "Something went wrong, and the current site cache for the " . $driver->name() . " driver was not cleaned successfully." );
  489. }
  490. }
  491. unset( $items );
  492. return;
  493. }
  494. else
  495. {
  496. if ( $driver->clear() === false )
  497. {
  498. $this->log_debug_message( __METHOD__, "Something went wrong, and the cache for the " . $driver->name() . " driver was not cleaned successfully." );
  499. }
  500. else
  501. {
  502. $this->log_debug_message( __METHOD__, "The cache for the " . $driver->name() . " driver was cleaned successfully." );
  503. }
  504. }
  505. }
  506. }
  507. /**
  508. * Deprecated. Doesn't return anything.
  509. * @return mixed
  510. */
  511. public function cache_info()
  512. {
  513. return $this->EE->TMPL->no_results();
  514. }
  515. /**
  516. * Breaks the cache. This method is an EE action (called from the CE Cache extension).
  517. *
  518. * @return void
  519. */
  520. public function break_cache()
  521. {
  522. //debug mode
  523. if ( $this->EE->input->get_post( 'act_test', true ) === 'y' )
  524. {
  525. $this->EE->lang->loadfile( 'ce_cache' );
  526. echo lang('ce_cache_debug_working');
  527. exit();
  528. }
  529. //this method is not intended to be called as an EE template tag
  530. if ( isset( $this->EE->TMPL ) )
  531. {
  532. return;
  533. }
  534. //load the cache break class, if needed
  535. if ( ! class_exists( 'Ce_cache_break' ) )
  536. {
  537. include PATH_THIRD . 'ce_cache/libraries/Ce_cache_break.php';
  538. }
  539. //instantiate the class break and call the break cache method
  540. $cache_break = new Ce_cache_break();
  541. $cache_break->break_cache_hook( null, null );
  542. }
  543. /**
  544. * Simple method to log a debug message to the EE Debug console.
  545. *
  546. * @param string $method
  547. * @param string $message
  548. * @return void
  549. */
  550. private function log_debug_message( $method = '', $message = '' )
  551. {
  552. if ( $this->debug )
  553. {
  554. $this->EE->TMPL->log_item( "&nbsp;&nbsp;***&nbsp;&nbsp;CE Cache $method debug: " . $message );
  555. }
  556. }
  557. /**
  558. * Gets the user-specified drivers array
  559. *
  560. * @param bool $allow_static
  561. * @return array
  562. */
  563. private function get_drivers_array( $allow_static = false, $override_prevent = false )
  564. {
  565. //get the user-specified drivers
  566. $drivers = $this->determine_setting( 'drivers', '' );
  567. if ( ! $allow_static )
  568. {
  569. //make sure the static driver is not included
  570. $drivers = str_replace( 'static', '', $drivers );
  571. }
  572. if ( ! empty( $drivers ) ) //we have driver settings
  573. {
  574. if ( ! is_array( $drivers ) )
  575. {
  576. $drivers = explode( '|', $drivers );
  577. }
  578. }
  579. else //no drivers specified, see if we have some legacy settings
  580. {
  581. $drivers = array();
  582. //determine the adapter
  583. $adapter = $this->determine_setting( 'adapter' );
  584. if ( ! empty( $adapter ) ) //if not set to a valid value, set to 'file'
  585. {
  586. $drivers[] = $adapter;
  587. }
  588. //determine the backup adapter
  589. $backup = $this->determine_setting( 'backup' );
  590. if ( ! empty( $backup ) ) //if not set to a valid value, set to 'dummy'
  591. {
  592. $drivers[] = $backup;
  593. }
  594. }
  595. if ( count( $drivers ) == 0 ) //still no drivers specified, default to 'file'
  596. {
  597. $drivers[] = 'file';
  598. }
  599. //is the user logged in?
  600. $logged_in = ($this->EE->session->userdata['member_id'] != 0);
  601. //see if there is a reason to prevent caching the current page (like the current page is a better workflow draft, or ce cache is off)
  602. if ( ! $override_prevent
  603. && (
  604. ( isset( $this->EE->session->cache['ep_better_workflow']['is_preview'] ) && $this->EE->session->cache['ep_better_workflow']['is_preview'] === true ) //better workflow draft
  605. || ( isset( $_GET['bwf_dp'] ) && $_GET['bwf_dp'] == 't' ) //another bwf check (from Matt Green)
  606. || ( isset( $_GET['publisher_status'] ) && $_GET['publisher_status'] == 'draft' ) // publisher check (from Fusionary)
  607. || $this->ee_string_to_bool( $this->determine_setting( 'off', 'no' ) ) //cache is off
  608. || ($this->ee_string_to_bool( $this->determine_setting( 'logged_in_only', 'no', 'fragment' ) ) && ! $logged_in ) //logged in only, but not logged in
  609. || ($this->ee_string_to_bool( $this->determine_setting( 'logged_out_only', 'no', 'fragment' ) ) && $logged_in ) //logged out only, but is logged in
  610. || ( ! empty( $_POST ) && $this->ee_string_to_bool( $this->determine_setting( 'ignore_post_requests', 'yes' ) ) && $_POST != array( 'entry_id' => '' ) ) //a POST page and ignore_post_requests is set to "yes"
  611. )
  612. )
  613. {
  614. //set the drivers to dummy
  615. $drivers = array( 'dummy' );
  616. }
  617. return $drivers;
  618. }
  619. /**
  620. * Loads the cache factory class, if needed
  621. */
  622. private function include_factory()
  623. {
  624. //load the class if needed
  625. if ( ! class_exists( 'Ce_cache_factory' ) )
  626. {
  627. include PATH_THIRD . 'ce_cache/libraries/Ce_cache_factory.php';
  628. }
  629. }
  630. /**
  631. * Sets up the cache if needed. This is its own method, as opposed to being in the constructor, because some methods will not need it.
  632. */
  633. private function setup_cache()
  634. {
  635. if ( ! $this->is_cache_setup ) //only run if the flag indicated it has not already been setup
  636. {
  637. //set the set up flag
  638. $this->is_cache_setup = true;
  639. //get the driver classes
  640. $drivers = $this->get_drivers_array();
  641. $this->include_factory();
  642. $this->drivers = Ce_cache_factory::factory( $drivers, true );
  643. //get the site name
  644. $site = Ce_cache_utils::get_site_label();
  645. $this->id_prefix = 'ce_cache/' . $site;
  646. if ( $this->EE->TMPL->fetch_param( 'global' ) == 'yes' ) //global cache
  647. {
  648. $this->id_prefix .= '/global/';
  649. }
  650. else //page specific cache
  651. {
  652. $this->determine_cache_url();
  653. //set the id prefix
  654. $this->id_prefix .= '/local/' . $this->EE->security->sanitize_filename( $this->cache_url, true );
  655. }
  656. $this->id_prefix = trim( $this->id_prefix, '/' ) . '/';
  657. }
  658. }
  659. private function determine_cache_url()
  660. {
  661. $override = $this->EE->TMPL->fetch_param( 'url_override' );
  662. if ( $override != false )
  663. {
  664. $this->cache_url = $override;
  665. }
  666. else
  667. {
  668. //triggers:original_uri
  669. if ( isset( $this->EE->config->_global_vars[ 'triggers:original_paginated_uri' ] ) ) //Zoo Triggers hijacked the URL
  670. {
  671. $this->cache_url = $this->EE->config->_global_vars[ 'triggers:original_paginated_uri' ];
  672. }
  673. else if ( isset( $this->EE->config->_global_vars[ 'freebie_original_uri' ] ) ) //Freebie hijacked the URL
  674. {
  675. $this->cache_url = $this->EE->config->_global_vars[ 'freebie_original_uri' ];
  676. }
  677. else //the URL was not hijacked
  678. {
  679. $this->cache_url = $this->EE->uri->uri_string();
  680. }
  681. }
  682. $prefix = $this->determine_setting('url_prefix', '');
  683. if ( ! empty( $prefix ) )
  684. {
  685. $this->cache_url = rtrim($prefix, '/') . '/' . $this->cache_url;
  686. }
  687. //UTF-8 decode any special characters
  688. $this->cache_url = utf8_decode( $this->cache_url );
  689. }
  690. /**
  691. * Determines the given setting by checking for the param, and then for the global var, and then for the config item.
  692. * @param string $name The name of the parameter. The string 'ce_cache_' will automatically be prepended for the global and config setting checks.
  693. * @param string $default The default setting value
  694. * @param string $long_prefix
  695. * @return string The setting value if found, or the default setting if not found.
  696. */
  697. private function determine_setting( $name, $default = '', $long_prefix = '' )
  698. {
  699. if ( ! empty( $long_prefix ) )
  700. {
  701. $long_prefix = $long_prefix . '_';
  702. }
  703. $long_name = 'ce_cache_' . $long_prefix . $name;
  704. if ( $this->EE->TMPL->fetch_param( $name ) !== false ) //param
  705. {
  706. $default = $this->EE->TMPL->fetch_param( $name );
  707. }
  708. else if ( isset( $this->EE->config->_global_vars[ $long_name ] ) && $this->EE->config->_global_vars[ $long_name ] !== false ) //first check global array
  709. {
  710. $default = $this->EE->config->_global_vars[ $long_name ];
  711. }
  712. else if ( $this->EE->config->item( $long_name ) !== false ) //then check config
  713. {
  714. $default = $this->EE->config->item( $long_name );
  715. }
  716. return $default;
  717. }
  718. /**
  719. * Parses the tagdata as if it were a template.
  720. * @param string $tagdata
  721. * @return string
  722. */
  723. public function parse_as_template( $tagdata = '' )
  724. {
  725. //store the current template object
  726. $TMPL2 = $this->EE->TMPL;
  727. unset($this->EE->TMPL);
  728. //create a new template object
  729. $temp = new EE_Template();
  730. $this->EE->TMPL = $temp;
  731. $temp->start_microtime = $TMPL2->start_microtime;
  732. $temp->template = '';
  733. $temp->final_template = '';
  734. $temp->fl_tmpl = '';
  735. $temp->tag_data = array();
  736. $temp->var_single = array();
  737. $temp->var_cond = array();
  738. $temp->var_pair = array();
  739. $temp->plugins = $TMPL2->plugins;
  740. $temp->modules = $TMPL2->modules;
  741. $temp->loop_count = 0;
  742. $temp->depth = 0;
  743. $temp->parse_tags();
  744. $temp->process_tags();
  745. //parse the current tagdata
  746. $temp->parse( $tagdata );
  747. //get the parsed tagdata back
  748. $tagdata = $temp->final_template;
  749. if ( $this->debug )
  750. {
  751. //these first items are boilerplate, and were already included in the first log - like "Parsing Site Variables", Snippet keys and values, etc
  752. unset( $temp->log[0], $temp->log[1], $temp->log[2], $temp->log[3], $temp->log[4], $temp->log[5], $temp->log[6] );
  753. $TMPL2->log = array_merge( $TMPL2->log, $temp->log );
  754. }
  755. //now let's check to see if this page is a 404 page
  756. if ( $this->is_404() )
  757. {
  758. $this->EE->output->out_type = '404';
  759. $this->EE->TMPL->template_type = '404';
  760. $this->EE->TMPL->final_template = $this->unescape_tagdata( $tagdata );
  761. $this->EE->TMPL->cease_processing = true;
  762. $this->EE->TMPL->no_results();
  763. $this->EE->session->cache[ 'Ce_cache' ]['is_404'] = true;
  764. }
  765. //restore the original template object
  766. $this->EE->TMPL = $TMPL2;
  767. unset($TMPL2, $temp);
  768. //call the post parse hook for this data
  769. if ( $this->EE->extensions->active_hook( 'template_post_parse' ) )
  770. {
  771. $tagdata = $this->EE->extensions->call( 'template_post_parse', $tagdata, false, $this->EE->config->item('site_id') );
  772. }
  773. //return the tagdata
  774. return $tagdata;
  775. }
  776. /**
  777. * Determines whether or not EE has 404 headers set
  778. */
  779. private function is_404()
  780. {
  781. if ( isset( $this->EE->output->headers[0] ) )
  782. {
  783. foreach ( $this->EE->output->headers as $value )
  784. {
  785. foreach ( $value as $v )
  786. {
  787. if ( strpos( $v, '404' ) !== false ) // a 404 header was found
  788. {
  789. return true;
  790. }
  791. }
  792. }
  793. }
  794. return false;
  795. }
  796. /**
  797. * Parses segment and global variables. Used to parse data in escape tags.
  798. * @param string $str
  799. * @return mixed
  800. */
  801. public function parse_vars( $str )
  802. {
  803. //remove the comments
  804. $str = $this->EE->TMPL->remove_ee_comments( $str );
  805. //parse segment variables
  806. if ( strpos( $str, '{segment_' ) !== false )
  807. {
  808. for ( $i = 1; $i < 10; $i++ )
  809. {
  810. $str = str_replace( '{segment_' . $i . '}', $this->EE->uri->segment( $i ), $str );
  811. }
  812. }
  813. //parse global variables
  814. $str = $this->EE->TMPL->parse_variables_row( $str, $this->EE->config->_global_vars );
  815. //parse current_time
  816. $str = $this->current_time( $str );
  817. return $str;
  818. }
  819. /**
  820. * Helper method that simplifies the data parsing and returning process.
  821. *
  822. * @param string $str
  823. * @return string
  824. */
  825. public function process_return_data( $str )
  826. {
  827. //parse globals and segment variables in case there were escaped during parsing
  828. $str = $this->parse_vars( $str );
  829. //parse current_time
  830. $str = $this->current_time( $str );
  831. //insert the action ids
  832. $str = $this->insert_action_ids( $str );
  833. return $str;
  834. }
  835. /**
  836. * Replaces the {current_time} variable with the actual current time. Useful if the variable was escaped. This method mimics the functionality from the Template class.
  837. *
  838. * @param string $str
  839. * @return string
  840. */
  841. public function current_time( $str )
  842. {
  843. if ( strpos( $str, '{current_time' ) === false )
  844. {
  845. return $str;
  846. }
  847. if ( preg_match_all( '/{current_time\s+format=([\"\'])([^\\1]*?)\\1}/', $str, $matches ) )
  848. {
  849. for ($j = 0; $j < count($matches[0]); $j++)
  850. {
  851. if ( version_compare( APP_VER, '2.6.0', '<' ) )
  852. {
  853. $str = str_replace($matches[0][$j], $this->EE->localize->decode_date($matches[2][$j], $this->EE->localize->now), $str);
  854. }
  855. else
  856. {
  857. $str = str_replace($matches[0][$j], $this->EE->localize->format_date($matches[2][$j]), $str);
  858. }
  859. }
  860. }
  861. return str_replace( '{current_time}', $this->EE->localize->now, $str);
  862. }
  863. /**
  864. * The following is a lot like the Functions method of inserting the action ids, except that this will first find the actions. The original method does not look to find the ids on cached data (it just stores them in an array as they are called).
  865. *
  866. * @param string $str
  867. * @return string
  868. */
  869. public function insert_action_ids( $str )
  870. {
  871. //will hold the actions
  872. $actions = array();
  873. //do we need to check for actions?
  874. if ( strpos( $str, LD . 'AID:' ) !== false && preg_match_all( '@' . LD . 'AID:([^:}]*):([^:}]*)' . RD . '@Us', $str, $matches, PREG_SET_ORDER ) ) //actions found
  875. {
  876. foreach ( $matches as $match )
  877. {
  878. $actions[ $match[ 1 ] ] = $match[ 2 ];
  879. }
  880. }
  881. else //no actions to parse
  882. {
  883. return $str;
  884. }
  885. //create the sql
  886. $sql = "SELECT action_id, class, method FROM exp_actions WHERE";
  887. foreach ( $actions as $key => $value )
  888. {
  889. $sql .= " (class= '" . $this->EE->db->escape_str( $key ) . "' AND method = '" . $this->EE->db->escape_str( $value ) . "') OR";
  890. }
  891. //run the query
  892. $query = $this->EE->db->query( substr( $sql, 0, -3 ) );
  893. if ( $query->num_rows() > 0 )
  894. {
  895. foreach ( $query->result_array() as $row )
  896. {
  897. $str = str_replace( LD . 'AID:' . $row[ 'class' ] . ':' . $row[ 'method' ] . RD, $row[ 'action_id' ], $str );
  898. }
  899. }
  900. return $str;
  901. }
  902. /**
  903. * Determines the id to use.
  904. *
  905. * @param string $method The calling method.
  906. * @return string|bool The id on success, or false on failure.
  907. */
  908. public function fetch_id( $method )
  909. {
  910. if ( $this->EE->TMPL->fetch_param( 'global' ) == 'yes' ) //global cache
  911. {
  912. $id = $this->EE->TMPL->fetch_param( 'id', '' );
  913. }
  914. else //page specific cache
  915. {
  916. $id = $this->determine_setting( 'id', 'item' );
  917. }
  918. $id = trim( $id );
  919. //get the id
  920. if ( empty( $id ) )
  921. {
  922. $this->log_debug_message( $method, "An id was not specified." );
  923. return false;
  924. }
  925. if ( ! $this->id_is_valid( $id ) )
  926. {
  927. $this->log_debug_message( $method, "The specified id '{$id}' is invalid. An id may only contain alpha-numeric characters, dashes, and underscores." );
  928. return false;
  929. }
  930. //add the id prefix
  931. return trim( Ce_cache_utils::remove_duplicate_slashes( $this->id_prefix . $id ), '/' );
  932. }
  933. /**
  934. * Validates an id.
  935. *
  936. * @param string $id
  937. * @return int 1 for valid, 0 for invalid
  938. */
  939. public function id_is_valid( $id )
  940. {
  941. return preg_match( '@[^\s]+@i', $id );
  942. }
  943. /**
  944. * Little helper method to convert parameters to a boolean value.
  945. *
  946. * @param $string
  947. * @return bool
  948. */
  949. public function ee_string_to_bool( $string )
  950. {
  951. return ( $string == 'y' || $string == 'yes' || $string == 'on' || $string === true );
  952. }
  953. public function no_results_tagdata()
  954. {
  955. //$tagdata = $this->EE->TMPL->tagdata;
  956. $index = 0;
  957. foreach ( $this->EE->TMPL->tag_data as $i => $tag_dat )
  958. {
  959. if ( $this->EE->TMPL->tagchunk == $tag_dat['chunk'] )
  960. {
  961. $index = $i;
  962. }
  963. }
  964. return $this->EE->TMPL->tag_data[$index]['block'];
  965. }
  966. /**
  967. * This is a shutdown function registered when an item is saved.
  968. */
  969. public function shut_it_down()
  970. {
  971. //determine if this is a 404 page
  972. $is_404 = (
  973. $this->EE->config->item( 'ce_cache_exclude_404s' ) != 'no' //we are not excluding 404 pages (in other words, we are caching 404 pages)
  974. && (
  975. isset( $this->EE->session->cache[ 'Ce_cache' ]['is_404'] ) //if previously evaluated to be a 404 page by fragment caching
  976. || $this->EE->output->out_type == '404' //or if the output type is set to a 404 page
  977. || $this->is_404() //or if there is a 404 header
  978. )
  979. );
  980. if ( $is_404 ) //this is a 404 page
  981. {
  982. //if there are cached items, and there are drivers, let's delete the cached items
  983. if ( isset ( $this->EE->session->cache[ 'Ce_cache' ]['cached_items'] ) && ! empty( $this->drivers ) )
  984. {
  985. //loop through each driver
  986. foreach ( $this->drivers as $driver )
  987. {
  988. foreach ( $this->EE->session->cache[ 'Ce_cache' ]['cached_items'] as $index => $item )
  989. {
  990. if ( $item['driver'] == $driver->name() )
  991. {
  992. $driver->delete( $item['id'] );
  993. unset( $this->EE->session->cache[ 'Ce_cache' ]['cached_items'][$index] );
  994. }
  995. }
  996. }
  997. }
  998. //remove the cached items from memory (although this will probably happen soon anyway)
  999. unset( $this->EE->session->cache[ 'Ce_cache' ]['cached_items'] );
  1000. }
  1001. else //this is not a 404 page (or it is a 404 page, but the config setting says not to exclude them)
  1002. {
  1003. if ( isset( $this->EE->session->cache[ 'Ce_cache' ][ 'static' ] ) ) //we have a static page, let's cache it
  1004. {
  1005. //load the class if needed
  1006. $this->include_factory();
  1007. //setup the driver
  1008. $drivers = Ce_cache_factory::factory( 'static' );
  1009. foreach ( $drivers as $driver )
  1010. {
  1011. $id = 'ce_cache/' . Ce_cache_utils::get_site_label() . '/static/' . $this->EE->security->sanitize_filename( $this->cache_url, true );
  1012. //get the final template
  1013. $final = $this->EE->TMPL->final_template;
  1014. //handle feed issues
  1015. if ( $this->EE->output->out_type == 'feed' )
  1016. {
  1017. //this normally happens in the output class, so we need to take care of it here
  1018. $final = preg_replace( '@<ee\:last_update>(.*?)<\/ee\:last_update>@', '', $final );
  1019. $final = preg_replace( '@{\?xml(.+?)\?}@', '<?xml\\1?'.'>', $final);
  1020. }
  1021. //pre save hook
  1022. if ( $this->EE->extensions->active_hook( 'ce_cache_pre_save' ) )
  1023. {
  1024. $final = $this->EE->extensions->call('ce_cache_pre_save', $final, 'static');
  1025. }
  1026. //get the headers
  1027. $headers = array();
  1028. //get the headers
  1029. if ( isset( $this->EE->output->headers ) && is_array( $this->EE->output->headers ) )
  1030. {
  1031. foreach( $this->EE->output->headers as $header )
  1032. {
  1033. if ( isset( $header[0] ) )
  1034. {
  1035. $headers[] = $header[0];
  1036. }
  1037. }
  1038. }
  1039. //attempt to save the cache
  1040. if ( $driver->set( $id, $final, $this->EE->session->cache[ 'Ce_cache' ][ 'static' ][ 'seconds' ], $headers ) === false ) //save unsuccessful
  1041. {
  1042. //probably too late to log debug messages - oh well
  1043. $this->log_debug_message( __METHOD__, "Something went wrong and the data for '{$this->cache_url}' was not cached using the " . $driver->name() . " driver." );
  1044. }
  1045. else //save successful
  1046. {
  1047. //probably too late to log debug messages - oh well
  1048. $this->log_debug_message( __METHOD__, "The data for '{$this->cache_url}' was successfully cached using the " . $driver->name() . " driver." );
  1049. //get the tags
  1050. $tags = $this->EE->session->cache[ 'Ce_cache' ][ 'static' ][ 'tags' ];
  1051. //add in the tags
  1052. if ( isset( $this->EE->session->cache[ 'Ce_cache' ]['tags'] ) )
  1053. {
  1054. $tags .= $this->EE->session->cache[ 'Ce_cache' ]['tags'];
  1055. }
  1056. $this->save_tags( $driver->clean_id( $id ), $tags );
  1057. }
  1058. }
  1059. }
  1060. }
  1061. }
  1062. /**
  1063. * A very simple method to attempt to determine if the current user agent is a bot
  1064. *
  1065. * @return bool
  1066. */
  1067. public function is_bot()
  1068. {
  1069. if ( ! isset( $this->EE->session->cache[ 'Ce_cache' ][ 'is_bot' ] ) )
  1070. {
  1071. $user_agent = $this->EE->input->user_agent();
  1072. $this->EE->session->cache[ 'Ce_cache' ][ 'is_bot' ] = (bool)( ! empty( $user_agent ) && preg_match( '@bot|spider|crawl|curl@i', $user_agent ) );
  1073. }
  1074. return $this->EE->session->cache[ 'Ce_cache' ][ 'is_bot' ];
  1075. }
  1076. /**
  1077. * Swaps out placeholders with their escaped values.
  1078. *
  1079. * @param null $tagdata
  1080. * @return mixed|null
  1081. */
  1082. public function unescape_tagdata( $tagdata = null )
  1083. {
  1084. if ( ! isset( $tagdata ) )
  1085. {
  1086. $tagdata = $this->no_results_tagdata();
  1087. }
  1088. //unescape any content escaped by the escape() method
  1089. if ( isset( $this->EE->session->cache[ 'Ce_cache' ]['placeholder-keys'] ) )
  1090. {
  1091. $tagdata = str_replace( $this->EE->session->cache[ 'Ce_cache' ]['placeholder-keys'], $this->EE->session->cache[ 'Ce_cache' ]['placeholder-values'], $tagdata);
  1092. $tagdata = str_replace( '{::segment_', '{segment_', $tagdata );
  1093. }
  1094. //unescape any escaped logged_in and logged_out conditionals if they were escaped above
  1095. if ( $this->EE->TMPL->fetch_param( 'process' ) != 'no' )
  1096. {
  1097. //now we'll swap the logged_in and logged_out variables back to their old selves
  1098. $tagdata = str_replace( array( 'ce_cache-in_logged', 'ce_cache-out_logged' ), array( 'logged_in', 'logged_out' ), $tagdata );
  1099. }
  1100. return $tagdata;
  1101. }
  1102. /**
  1103. * Saves any tags that are specified in the tag parameter.
  1104. *
  1105. * @param string $id
  1106. * @param string/bool $tag_string The string from $this->EE->TMPL->fetch_param( 'tags' )
  1107. */
  1108. public function save_tags( $id, $tag_string = '' )
  1109. {
  1110. //tag the content if applicable
  1111. if ( $tag_string !== false )
  1112. {
  1113. //cleanup the tag string
  1114. $tag_string = $this->reduce_pipes( $tag_string );
  1115. //explode into tags
  1116. $temps = explode( '|', $tag_string );
  1117. $data = array();
  1118. //loop through the items
  1119. foreach ( $temps as $temp )
  1120. {
  1121. $temp = trim( $temp );
  1122. if ( empty( $temp ) )
  1123. {
  1124. $this->log_debug_message( __METHOD__, 'An empty tag was found and will not be applied to the saved item "' . $id . '".' );
  1125. continue;
  1126. }
  1127. if ( strlen( $temp ) > 100 )
  1128. {
  1129. $this->log_debug_message( __METHOD__, 'The tag "' . $temp . '" could not be saved for the "' . $id . '" item, because it is over 100 characters long.' );
  1130. continue;
  1131. }
  1132. $data[] = array( 'item_id' => $id, 'tag' => $temp );
  1133. }
  1134. unset( $temps );
  1135. //delete all of the current tags for this item
  1136. $this->EE->db->query( 'DELETE FROM exp_ce_cache_tagged_items WHERE item_id = ?', array( $id ) );
  1137. //add in the new tags
  1138. if ( count( $data ) > 1 )
  1139. {
  1140. $this->EE->db->insert_batch( 'ce_cache_tagged_items', $data );
  1141. }
  1142. else if ( count( $data ) > 0 )
  1143. {
  1144. $this->EE->db->insert( 'ce_cache_tagged_items', $data[0] );
  1145. }
  1146. unset( $data );
  1147. }
  1148. }
  1149. private function reduce_pipes( $string, $make_lowercase = true )
  1150. {
  1151. $string = trim( $string, '|' ); //trim pipes
  1152. $string = str_replace( '||', '', $string ); //remove double pipes (empty tags)
  1153. if ( $make_lowercase )
  1154. {
  1155. $string = strtolower( $string ); //convert to lowercase
  1156. }
  1157. return $string;
  1158. }
  1159. }
  1160. /* End of file mod.ce_cache.php */
  1161. /* Location: /system/expressionengine/third_party/ce_cache/mod.ce_cache.php */