PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/mu-plugins/pantheon/pantheon-cache.php

https://gitlab.com/Blueprint-Marketing/WordPress-1
PHP | 437 lines | 325 code | 31 blank | 81 comment | 8 complexity | 148741b9949b0e1b9cc0fc0fd97669a7 MD5 | raw file
  1. <?php
  2. /* This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program; if not, write to the Free Software
  12. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  13. */
  14. class Pantheon_Cache {
  15. /**
  16. * Define the capability required to see and modify the settings page.
  17. *
  18. * @var string
  19. */
  20. public $options_capability = 'manage_options';
  21. /**
  22. * Define the default options, which are overridden by what's in wp_options.
  23. *
  24. * @var array
  25. */
  26. public $default_options = array();
  27. /**
  28. * Stores the options for this plugin (from wp_options).
  29. *
  30. * @var array
  31. */
  32. public $options = array();
  33. /**
  34. * Store the Paths to be flushed at shutdown.
  35. *
  36. * @var array
  37. */
  38. public $paths = array();
  39. /**
  40. * The slug for the plugin, used in various places like the options page.
  41. */
  42. const SLUG = 'pantheon-cache';
  43. /**
  44. * Holds the singleton instance.
  45. *
  46. * @static
  47. * @var object
  48. */
  49. protected static $instance;
  50. /**
  51. * Get a reference to the singleton.
  52. *
  53. * @return object The singleton instance.
  54. */
  55. public static function instance() {
  56. if ( ! isset( self::$instance ) ) {
  57. self::$instance = new Pantheon_Cache;
  58. self::$instance->setup();
  59. }
  60. return self::$instance;
  61. }
  62. protected function __construct() {
  63. /** Don't do anything **/
  64. }
  65. /**
  66. * Setup the actions and filters we need to hook into, and initialize any properties we need.
  67. *
  68. * @return void
  69. */
  70. protected function setup() {
  71. $this->options = get_option( self::SLUG, array() );
  72. $this->default_options = array(
  73. 'default_ttl' => 600
  74. );
  75. $this->options = wp_parse_args( $this->options, $this->default_options );
  76. add_action( 'admin_init', array( $this, 'action_admin_init' ) );
  77. add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
  78. add_action( 'clean_post_cache', array( $this, 'clean_post_cache' ) );
  79. add_action( 'clean_term_cache', array( $this, 'clean_term_cache' ), 10, 2 );
  80. add_action( 'admin_post_pantheon_cache_delete_page', array( $this, 'clean_specific_page' ) );
  81. add_action( 'admin_post_pantheon_cache_flush_site', array( $this, 'flush_site' ) );
  82. if ( ! is_admin() ) {
  83. add_action( 'send_headers', array( $this, 'cache_add_headers' ) );
  84. add_action( 'wp_before_admin_bar_render', array( $this, 'cache_admin_bar_render' ) );
  85. }
  86. add_action( 'shutdown', array( $this, 'cache_clean_urls' ), 999 );
  87. }
  88. /**
  89. * Prep the Settings API.
  90. *
  91. * @return void
  92. */
  93. public function action_admin_init() {
  94. register_setting( self::SLUG, self::SLUG, array( self::$instance, 'sanitize_options' ) );
  95. add_settings_section( 'general', false, '__return_false', self::SLUG );
  96. add_settings_field( 'default_ttl', __( 'Default Cache Time', 'pantheon-cache' ), array( self::$instance, 'default_ttl_field' ), self::SLUG, 'general' );
  97. }
  98. /**
  99. * Add the settings page to the menu.
  100. *
  101. * @return void
  102. */
  103. public function action_admin_menu() {
  104. add_options_page( __( 'Pantheon Cache', 'pantheon-cache' ), __( 'Pantheon Cache', 'pantheon-cache' ), $this->options_capability, self::SLUG, array( self::$instance, 'view_settings_page' ) );
  105. }
  106. /**
  107. * Add the HTML for the default TTL field.
  108. *
  109. * @return void
  110. */
  111. public function default_ttl_field() {
  112. echo '<input type="text" name="' . self::SLUG . '[default_ttl]" value="' . $this->options['default_ttl'] . '" size="5" /> ' . __( 'seconds', 'pantheon-cache' );
  113. }
  114. /**
  115. * Sanitize our options.
  116. *
  117. * @param array $in The POST values.
  118. * @return array The sanitized POST values.
  119. */
  120. public function sanitize_options( $in ) {
  121. $out = $this->default_options;
  122. // Validate default_ttl
  123. $out['default_ttl'] = absint( $in['default_ttl'] );
  124. if ( ! $out['default_ttl'] )
  125. $out['default_ttl'] = 600;
  126. return $out;
  127. }
  128. /**
  129. * Output the settings page.
  130. *
  131. * @return void
  132. */
  133. public function view_settings_page() {
  134. ?>
  135. <div class="wrap">
  136. <h2><?php _e( 'Pantheon Cache', 'pantheon-cache' ); ?></h2>
  137. <?php if ( ! empty( $_GET['cache-cleared'] ) && 'true' == $_GET['cache-cleared'] ) : ?>
  138. <div class="updated below-h2">
  139. <p><?php esc_html_e( 'Site cache flushed.', 'pantheon-cache' ); ?></p>
  140. </div>
  141. <?php endif ?>
  142. <h3><?php _e( 'General Settings', 'pantheon-cache' ); ?></h3>
  143. <form action="options.php" method="POST">
  144. <?php settings_fields( self::SLUG ); ?>
  145. <?php do_settings_sections( self::SLUG ); ?>
  146. <?php submit_button(); ?>
  147. </form>
  148. <?php if ( apply_filters( 'pantheon_cache_allow_clear_all', true ) ) : ?>
  149. <hr />
  150. <form action="admin-post.php" method="POST">
  151. <input type="hidden" name="action" value="pantheon_cache_flush_site" />
  152. <?php wp_nonce_field( 'pantheon-cache-clear-all', 'pantheon-cache-nonce' ); ?>
  153. <h3><?php _e( 'Clear Site Cache', 'pantheon-cache' ); ?></h3>
  154. <p><?php _e( "Clear the cache for the entire site. Use with care, as it will negatively impact your site's performance for a short period of time.", 'pantheon-cache' ); ?></p>
  155. <?php submit_button( __( 'Clear Cache', 'pantheon-cache' ), 'secondary' ); ?>
  156. </form>
  157. <?php endif ?>
  158. </div>
  159. <?php
  160. }
  161. /**
  162. * Add the cache-control header.
  163. *
  164. * @return void
  165. */
  166. public function cache_add_headers() {
  167. $ttl = absint( $this->options['default_ttl'] );
  168. if ( $ttl < 60 )
  169. $ttl = 600;
  170. header( 'cache-control: public, max-age=' . $ttl );
  171. }
  172. /**
  173. * Add the "Delete Cache" button to the admin bar.
  174. *
  175. * @return void
  176. */
  177. public function cache_admin_bar_render() {
  178. global $wp_admin_bar;
  179. if ( ! is_user_logged_in() )
  180. return false;
  181. if ( function_exists( 'current_user_can' ) && false == current_user_can( 'delete_others_posts' ) )
  182. return false;
  183. $wp_admin_bar->add_menu( array(
  184. 'parent' => '',
  185. 'id' => 'delete-cache',
  186. 'title' => __( 'Delete Cache', 'pantheon-cache' ),
  187. 'meta' => array( 'title' => __( 'Delete cache of the current page', 'pantheon-cache' ) ),
  188. 'href' => wp_nonce_url( admin_url( 'admin-post.php?action=pantheon_cache_delete_page&path=' . urlencode( preg_replace( '/[ <>\'\"\r\n\t\(\)]/', '', $_SERVER[ 'REQUEST_URI' ] ) ) ), 'delete-cache' )
  189. ) );
  190. }
  191. /**
  192. * Clear a specific path from cache. This handles the action from the admin bar button.
  193. *
  194. * @return void
  195. */
  196. public function clean_specific_page() {
  197. if ( ! function_exists( 'current_user_can' ) || false == current_user_can( 'delete_others_posts' ) )
  198. return false;
  199. if ( ! empty( $_REQUEST[ '_wpnonce' ] ) && wp_verify_nonce( $_REQUEST[ '_wpnonce' ], 'delete-cache' ) ) {
  200. $this->enqueue_urls( $_REQUEST['path'] );
  201. wp_redirect( preg_replace( '/[ <>\'\"\r\n\t\(\)]/', '', $_REQUEST['path'] ) );
  202. exit();
  203. }
  204. }
  205. /**
  206. * Clear the cache for the entire site.
  207. *
  208. * @return void
  209. */
  210. public function flush_site() {
  211. if ( ! function_exists( 'current_user_can' ) || false == current_user_can( 'manage_options' ) )
  212. return false;
  213. if ( ! empty( $_POST['pantheon-cache-nonce'] ) && wp_verify_nonce( $_POST['pantheon-cache-nonce'], 'pantheon-cache-clear-all' ) ) {
  214. $this->enqueue_regex( '/.*' );
  215. wp_redirect( admin_url( 'options-general.php?page=pantheon-cache&cache-cleared=true' ) );
  216. exit();
  217. }
  218. }
  219. /**
  220. * Clear the cache for a post.
  221. *
  222. * @param int $post_id A post ID to clean.
  223. * @return void
  224. */
  225. public function clean_post_cache( $post_id, $include_homepage = true ) {
  226. if ( get_post_type( $post_id ) == 'revision' || get_post_status( $post_id ) != 'publish' )
  227. return;
  228. $urls = array();
  229. $post_link = get_permalink( $post_id );
  230. if ( $post_link ) {
  231. $urls[] = $post_link;
  232. }
  233. if ( $include_homepage ) {
  234. $urls[] = get_option( 'home' );
  235. $urls[] = trailingslashit( get_option( 'home' ) );
  236. }
  237. $urls = apply_filters( 'pantheon_clean_post_cache', $urls, $post_id, $include_homepage );
  238. $this->enqueue_urls( $urls );
  239. }
  240. /**
  241. * Clear the cache for a given term or terms and taxonomy.
  242. *
  243. * @param int|array $ids Single or list of Term IDs.
  244. * @param string $taxonomy Can be empty and will assume tt_ids, else will use for context.
  245. * @return void
  246. */
  247. public function clean_term_cache( $term_ids, $taxonomy ) {
  248. $urls = array();
  249. foreach ( (array) $term_ids as $term_id ) {
  250. $term_link = get_term_link( intval( $term_id ), $taxonomy );
  251. if ( ! is_wp_error( $term_link ) ) {
  252. $urls[] = $term_link;
  253. }
  254. }
  255. $urls = apply_filters( 'pantheon_clean_term_cache', $urls, $term_ids, $taxonomy );
  256. $this->enqueue_urls( $urls );
  257. }
  258. /**
  259. * Clear the cache for a given term or terms and taxonomy.
  260. *
  261. * This is a placeholder and is not currently active.
  262. *
  263. * @param int|array $object_ids Single or list of term object ID(s).
  264. * @param array|string $object_type The taxonomy object type.
  265. * @return void
  266. */
  267. public function clean_object_term_cache( $object_ids, $object_type ) {
  268. $urls = array();
  269. if ( post_type_exists( $object_type ) ) {
  270. foreach ( (array) $object_ids as $post_id ) {
  271. $urls[] = get_permalink( $post_id );
  272. }
  273. }
  274. global $wp_rewrite;
  275. $taxonomies = get_object_taxonomies( $object_type );
  276. foreach ( $taxonomies as $taxonomy ) {
  277. $termlink = $wp_rewrite->get_extra_permastruct( $taxonomy );
  278. # Let's make sure that the taxonomy doesn't have a root-level permalink,
  279. # which is unlikely, but possible. If it did, this would clear the whole site.
  280. if ( preg_match( "#^.+/%$taxonomy%#i", $termlink ) ) {
  281. $urls[] = str_replace( "%$taxonomy%", '.*', $termlink );
  282. }
  283. }
  284. $urls = apply_filters( 'pantheon_clean_object_term_cache', $urls, $object_ids, $object_type );
  285. $this->enqueue_urls( $urls );
  286. }
  287. /**
  288. * Enqueue Fully-qualified urls to be cleared on shutdown.
  289. *
  290. * @param array|string $urls List of full urls to clear.
  291. * @return void
  292. */
  293. public function enqueue_urls( $urls ) {
  294. $paths = array();
  295. $urls = array_filter( (array) $urls, 'is_string' );
  296. foreach ( $urls as $full_url ) {
  297. # Parse down to the path+query, escape regex.
  298. $parsed = parse_url( $full_url );
  299. $path = $parsed['path'] . $parsed['query'];
  300. if ( '' == $path ) {
  301. continue;
  302. }
  303. $path = '^' . preg_quote( $path ) . '$';
  304. $paths[] = $path;
  305. }
  306. $this->paths = array_merge( $this->paths, $paths );
  307. }
  308. /**
  309. * Enqueue a regex to be cleared.
  310. *
  311. * You must understand regular expressions to use this, and be careful.
  312. *
  313. * @param string $regex path regex to clear.
  314. * @return void
  315. */
  316. public function enqueue_regex( $regex ) {
  317. $this->paths[] = $regex;
  318. }
  319. public function cache_clean_urls() {
  320. if ( empty( $this->paths ) )
  321. return;
  322. $this->paths = apply_filters( 'pantheon_clean_urls', array_unique( $this->paths ) );
  323. # Call the big daddy here
  324. $url = home_url();
  325. $host = parse_url( $url, PHP_URL_HOST );
  326. $this->paths = apply_filters( 'pantheon_final_clean_urls', $this->paths );
  327. if ( function_exists( 'pantheon_clear_edge' ) ) {
  328. pantheon_clear_edge( $host, $this->paths );
  329. }
  330. }
  331. }
  332. /**
  333. * Get a reference to the singleton.
  334. *
  335. * This can be used to reference public methods, e.g. `Pantheon_Cache()->clean_post_cache( 123 )`
  336. *
  337. * @return void
  338. */
  339. function Pantheon_Cache() {
  340. return Pantheon_Cache::instance();
  341. }
  342. add_action( 'plugins_loaded', 'Pantheon_Cache' );
  343. /**
  344. * @see Pantheon_Cache::clean_post_cache
  345. */
  346. function pantheon_clean_post_cache( $post_id, $include_homepage = true ) {
  347. Pantheon_Cache()->clean_post_cache( $post_id, $include_homepage );
  348. }
  349. /**
  350. * @see Pantheon_Cache::clean_term_cache
  351. */
  352. function pantheon_clean_term_cache( $term_ids, $taxonomy ) {
  353. Pantheon_Cache()->clean_term_cache( $term_ids, $taxonomy );
  354. }
  355. /**
  356. * @see Pantheon_Cache::enqueue_urls
  357. */
  358. function pantheon_enqueue_urls( $urls ) {
  359. Pantheon_Cache()->enqueue_urls( $urls );
  360. }