JezK
Edit File: cache.php
<?php /** Forked from Proxy Cache Purge 5.2.0 (https://github.com/ipstenu/varnish-http-purge/) and refactored to the needs of Raidboxes Varnish cache system. */ /** * Purge Class * * @since 2.0 */ class RBCachePlugin { /** * Version Number * * @var string */ public static $version = '5.2.0'; /** * List of URLs to be purged * * (default value: array()) * * @var array * @access protected */ protected $purge_urls = array(); /* RAIDBOXES_START */ // Posts already walked by purge_post() this request. One save reaches it // through four hooks, each re-querying the post's terms and author. protected $purged_post_ids = array(); // Term name and slug before edit_terms fired. See remember_term_before_edit(). protected $terms_before_edit = array(); /* RAIDBOXES_END */ /** * Devmode options * * (default value: array()) * * @var array * @access public * @static */ /* RAIDBOXES_START public static $devmode = array(); RAIDBOXES_END */ /** * Init * * @since 2.0 * @access public */ public function __construct() { /* RAIDBOXES_START defined( 'VHP_VARNISH_IP' ) || define( 'VHP_VARNISH_IP', false ); defined( 'VHP_DEVMODE' ) || define( 'VHP_DEVMODE', false ); defined( 'VHP_DOMAINS' ) || define( 'VHP_DOMAINS', false ); // Development mode defaults to off. self::$devmode = array( 'active' => false, 'expire' => time(), ); if ( ! get_site_option( 'vhp_varnish_devmode' ) ) { update_site_option( 'vhp_varnish_devmode', self::$devmode ); } // Default URL is home. if ( ! get_site_option( 'vhp_varnish_url' ) ) { update_site_option( 'vhp_varnish_url', $this->the_home_url() ); } // Default IP is nothing. if ( ! get_site_option( 'vhp_varnish_ip' ) && ! VHP_VARNISH_IP ) { update_site_option( 'vhp_varnish_ip', '' ); } // Default Debug is the home. if ( ! get_site_option( 'vhp_varnish_debug' ) ) { update_site_option( 'vhp_varnish_debug', array( $this->the_home_url() => array() ) ); } // Default Max posts to purge before purge all happens instead. if ( ! get_site_option( 'vhp_varnish_max_posts_before_all' ) ) { update_site_option( 'vhp_varnish_max_posts_before_all', 50 ); } RAIDBOXES_END */ // Release the hounds! add_action( 'init', array( &$this, 'init' ) ); add_action( 'admin_init', array( &$this, 'admin_init' ) ); /* RAIDBOXES_START add_action( 'import_start', array( &$this, 'import_start' ) ); add_action( 'import_end', array( &$this, 'import_end' ) ); RAIDBOXES_END */ // Check if there's an upgrade add_action( 'upgrader_process_complete', array( &$this, 'check_upgrades' ), 10, 2 ); } /** * Admin Init * * @since 4.1 * @access public */ public function admin_init() { global $pagenow; // If WordPress.com Master Bar is active, show the activity box. if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'masterbar' ) ) { add_action( 'activity_box_end', array( $this, 'varnish_rightnow' ), 100 ); } // Failure: Pre WP 4.7. if ( version_compare( get_bloginfo( 'version' ), '4.7', '<=' ) ) { deactivate_plugins( plugin_basename( __FILE__ ) ); add_action( 'admin_notices', array( $this, 'require_wp_version_notice' ) ); return; } // Admin notices. if ( current_user_can( 'manage_options' ) && 'site-health.php' !== $pagenow ) { // Warning: Debug is active. /* RAIDBOXES_START if ( VarnishDebug::devmode_check() ) { add_action( 'admin_notices', array( $this, 'devmode_is_active_notice' ) ); } RAIDBOXES_END */ // Warning: No Pretty Permalinks! if ( '' === get_site_option( 'permalink_structure' ) ) { add_action( 'admin_notices', array( $this, 'require_pretty_permalinks_notice' ) ); } } } /** * Plugin Init * * @since 1.0 * @access public */ public function init() { global $blog_id, $wp_db_version; // If the DB version we detect isn't the same as the version core thinks // we will flush DB cache. This may cause double dumping in some cases but // should not be harmful. if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && (int) get_option( 'db_version' ) !== $wp_db_version ) { wp_cache_flush(); } // If Dev Mode is true, kill caching. /* RAIDBOXES_START if ( VarnishDebug::devmode_check() ) { if ( ! is_admin() ) { // Sessions used to break PHP caching. // @codingStandardsIgnoreStart if ( ! is_user_logged_in() && session_status() != PHP_SESSION_ACTIVE ) { @session_start(); } // @codingStandardsIgnoreEnd // Add nocache to CSS and JS. add_filter( 'style_loader_src', array( 'VarnishDebug', 'nocache_cssjs' ), 10, 2 ); add_filter( 'script_loader_src', array( 'VarnishDebug', 'nocache_cssjs' ), 10, 2 ); } } RAIDBOXES_END */ // get my events. $events = $this->get_register_events(); $no_id_events = $this->get_no_id_events(); // make sure we have events and they're in an array. /* RAIDBOXES_START - Upstream knows only "has a post ID" and "flush everything". get_event_routes() adds a third case: events that carry an object we can resolve to specific URLs. */ if ( ! empty( $events ) ) { // Force it to be an array, in case someone's stupid. $events = (array) $events; $no_id_events = (array) $no_id_events; $routes = $this->get_event_routes(); // Add the action for each event. foreach ( $events as $event ) { if ( in_array( $event, $no_id_events, true ) ) { // These events have no post ID and, thus, will perform a full purge. add_action( $event, array( $this, 'execute_purge_no_id' ) ); } elseif ( isset( $routes[ $event ] ) ) { add_action( $event, array( $this, $routes[ $event ][0] ), 10, $routes[ $event ][1] ); } else { // Unrouted events are assumed to pass a post ID, as upstream assumes. add_action( $event, array( $this, 'purge_post' ), 10, 1 ); } } } // edit_terms fires before the update, edit_term after, so this is the only // chance to see whether purge_term() is looking at a rename. add_action( 'edit_terms', array( $this, 'remember_term_before_edit' ), 10, 2 ); /* RAIDBOXES_END */ add_action( 'shutdown', array( $this, 'execute_purge' ) ); // Success: Admin notice when purging. if ( ( isset( $_GET['vhp_flush_all'] ) && check_admin_referer( 'vhp-flush-all' ) ) || ( isset( $_GET['vhp_flush_do'] ) && check_admin_referer( 'vhp-flush-do' ) ) ) { /* RAIDBOXES_START if ( 'devmode' === $_GET['vhp_flush_do'] && isset( $_GET['vhp_set_devmode'] ) ) { VarnishDebug::devmode_toggle( esc_attr( $_GET['vhp_set_devmode'] ) ); add_action( 'admin_notices', array( $this, 'admin_message_devmode' ) ); } else { add_action( 'admin_notices', array( $this, 'admin_message_purge' ) ); } RAIDBOXES_END */ /* RAIDBOXES_START */ add_action( 'admin_notices', array( $this, 'admin_message_purge' ) ); /* RAIDBOXES_END */ } // Add Admin Bar. add_action( 'admin_bar_menu', array( $this, 'varnish_rightnow_adminbar' ), 100 ); add_action( 'admin_enqueue_scripts', array( $this, 'custom_css' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'custom_css' ) ); } /** * Check if something has upgraded and try to flush the DB cache. * This runs for ALL upgrades (theme, plugin, and core) to account for * the complex nature that are upgrades. * * @param array $object of upgrade data * @param array $options picked for upgrade * @since 4.8 */ public function check_upgrades( $object, $options ) { if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { wp_cache_flush(); } } /** * Pause caching if Importer was started * * @since 4.8 */ public function import_start() { /* RAIDBOXES_START VarnishDebug::devmode_toggle( 'activate' ); RAIDBOXES_END */ } /** * Resume caching if Importer has ended * * @since 4.8 */ public function import_end() { /* RAIDBOXES_START VarnishDebug::devmode_toggle( 'deactivate' ); RAIDBOXES_END */ } /** * Purge Message * Informs of a successful purge * * @since 4.6 */ public function admin_message_purge() { echo '<div id="message" class="notice notice-success fade is-dismissible"><p><strong>' . esc_html__( 'Cache emptied!', 'varnish-http-purge' ) . '</strong></p></div>'; } /** * Devmode Message * Informs of a toggle in Devmode * * @since 4.6 */ /* RAIDBOXES_START public function admin_message_devmode() { $message = ( VarnishDebug::devmode_check() ) ? __( 'Development Mode activated for the next 24 hours.', 'varnish-http-purge' ) : __( 'Development Mode deactivated.', 'varnish-http-purge' ); echo '<div id="message" class="notice notice-success fade is-dismissible"><p><strong>' . wp_kses_post( $message ) . '</strong></p></div>'; } RAIDBOXES_END */ /** * Require: Pretty Permalinks Message * Explains you need Pretty Permalinks enabled to use this plugin * * @since 2.0 */ public function require_pretty_permalinks_notice() { // translators: The URL should link to the permalinks page. echo wp_kses_post( '<div id="message" class="error"><p>' . sprintf( __( 'Proxy Cache Purge requires you to use custom permalinks. Please go to the <a href="%1$s">Permalinks Options Page</a> to configure them.', 'varnish-http-purge' ), esc_url( admin_url( 'options-permalink.php' ) ) ) . '</p></div>' ); } /** * Require: WP Version Message * Explains you need WordPress 4.7+ to use this plugin * * @since 4.1 */ public function require_wp_version_notice() { // translators: The URL should link to the update core page. echo "<div id='message' class='error'><p>" . sprintf( esc_html__( 'Proxy Cache Purge requires WordPress 4.7 or greater. Please <a href="%1$s">upgrade WordPress</a>.', 'varnish-http-purge' ), esc_url( admin_url( 'update-core.php' ) ) ) . '</p></div>'; } /** * Warning: Development Mode * Checks if DevMode is active * * @since 4.6.0 */ /* RAIDBOXES_START public function devmode_is_active_notice() { if ( VHP_DEVMODE ) { $message = __( 'Proxy Cache Purge Development Mode has been activated via wp-config.', 'varnish-http-purge' ); } else { $devmode = get_site_option( 'vhp_varnish_devmode', self::$devmode ); $time = human_time_diff( time(), $devmode['expire'] ); if ( ! $devmode['active'] ) { if ( ! is_multisite() ) { // translators: %1$s is the time until dev mode expires. // translators: %2$s is a link to the settings pages. $message = sprintf( __( 'Proxy Cache Purge Development Mode is active for the next %1$s. You can disable this at the <a href="%2$s">Proxy Settings Page</a>.', 'varnish-http-purge' ), $time, esc_url( admin_url( 'admin.php?page=varnish-page' ) ) ); } else { // translators: %1$s is the time until dev mode expires. $message = sprintf( __( 'Proxy Cache Purge Development Mode is active for the next %1$s.', 'varnish-http-purge' ), $time ); } } } // Only echo if there's actually a message if ( isset( $message ) ) { echo '<div class="notice notice-warning"><p>' . wp_kses_post( $message ) . '</p></div>'; } } RAIDBOXES_END */ /** * The Home URL * Get the Home URL and allow it to be filterable * This is for domain mapping plugins that, for some reason, don't filter * on their own (including WPMU, Ron's, and so on). * * @since 4.0 */ public static function the_home_url() { $home_url = apply_filters( 'vhp_home_url', home_url() ); return $home_url; } /** * Custom CSS to allow for colouring. * * @since 4.5.0 */ public function custom_css() { if ( is_user_logged_in() && is_admin_bar_showing() ) { wp_register_style( 'varnish_http_purge', plugins_url( 'style.css', __FILE__ ), false, self::$version ); wp_enqueue_style( 'varnish_http_purge' ); } } /** * Purge Button in the Admin Bar * * @access public * @param mixed $admin_bar - data passed back from admin bar. * @return void */ public function varnish_rightnow_adminbar( $admin_bar ) { global $wp; $can_purge = false; /* RAIDBOXES_START $cache_active = ( VarnishDebug::devmode_check() ) ? __( 'Inactive', 'varnish-http-purge' ) : __( 'Active', 'varnish-http-purge' ); // translators: %s is the state of cache. $cache_titled = sprintf( __( 'Cache (%s)', 'varnish-http-purge' ), $cache_active ); RAIDBOXES_END */ $cache_titled = sprintf( __( 'Cache', 'varnish-http-purge' ) ); if ( ( ! is_admin() && get_post() !== false && current_user_can( 'edit_published_posts' ) ) || current_user_can( 'activate_plugins' ) ) { // Main Array. $args = array( array( 'id' => 'purge-varnish-cache', 'title' => '<span class="ab-icon" style="background-image: url(' . self::get_icon_svg() . ') !important;"></span><span class="ab-label">' . $cache_titled . '</span>', 'meta' => array( 'class' => 'varnish-http-purge', ), ), ); $can_purge = true; } // Checking user permissions for who can and cannot use the all flush. if ( // SingleSite - admins can always purge. ( ! is_multisite() && current_user_can( 'activate_plugins' ) ) || // Multisite - Network Admin can always purge. current_user_can( 'manage_network' ) || // Multisite - Site admins can purge UNLESS it's a subfolder install and we're on site #1. ( is_multisite() && current_user_can( 'activate_plugins' ) && ( SUBDOMAIN_INSTALL || ( ! SUBDOMAIN_INSTALL && ( BLOG_ID_CURRENT_SITE !== $blog_id ) ) ) ) ) { $args[] = array( 'parent' => 'purge-varnish-cache', 'id' => 'purge-varnish-cache-all', 'title' => __( 'Purge Varnish cache (All Pages)', 'varnish-http-purge' ), 'href' => wp_nonce_url( add_query_arg( 'vhp_flush_do', 'all' ), 'vhp-flush-do' ), 'meta' => array( 'title' => __( 'Purge Varnish cache (All Pages)', 'varnish-http-purge' ), ), ); // If a memcached file is found, we can do this too. if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { $args[] = array( 'parent' => 'purge-varnish-cache', 'id' => 'purge-varnish-cache-db', 'title' => __( 'Purge object cache', 'varnish-http-purge' ), 'href' => wp_nonce_url( add_query_arg( 'vhp_flush_do', 'object' ), 'vhp-flush-do' ), 'meta' => array( 'title' => __( 'Purge object cache', 'varnish-http-purge' ), ), ); } // If Devmode is in the config, don't allow it to be disabled. /* RAIDBOXES_START if ( ! VHP_DEVMODE ) { // Populate enable/disable cache button. if ( VarnishDebug::devmode_check() ) { $purge_devmode_title = __( 'Restart Cache', 'varnish-http-purge' ); $vhp_add_query_arg = array( 'vhp_flush_do' => 'devmode', 'vhp_set_devmode' => 'deactivate', ); } else { $purge_devmode_title = __( 'Pause Cache (24h)', 'varnish-http-purge' ); $vhp_add_query_arg = array( 'vhp_flush_do' => 'devmode', 'vhp_set_devmode' => 'activate', ); } $args[] = array( 'parent' => 'purge-varnish-cache', 'id' => 'purge-varnish-cache-devmode', 'title' => $purge_devmode_title, 'href' => wp_nonce_url( add_query_arg( $vhp_add_query_arg ), 'vhp-flush-do' ), 'meta' => array( 'title' => $purge_devmode_title, ), ); } RAIDBOXES_END */ } // If we're on a front end page AND the current user can edit published posts, then they can do this. if ( ! is_admin() && get_post() !== false && current_user_can( 'edit_published_posts' ) ) { $page_url = esc_url( home_url( $wp->request ) ); $args[] = array( 'parent' => 'purge-varnish-cache', 'id' => 'purge-varnish-cache-this', 'title' => __( 'Purge Varnish cache (This Page)', 'varnish-http-purge' ), 'href' => wp_nonce_url( add_query_arg( 'vhp_flush_do', $page_url . '/' ), 'vhp-flush-do' ), 'meta' => array( 'title' => __( 'Purge Varnish cache (This Page)', 'varnish-http-purge' ), ), ); } if ( $can_purge ) { foreach ( $args as $arg ) { $admin_bar->add_node( $arg ); } } } /** * Get the icon as SVG. * * Forked from Yoast SEO * * @access public * @param bool $base64 (default: true) - Use SVG, true/false? * @param string $icon_color - What color to use. * @return string */ public static function get_icon_svg( $base64 = true, $icon_color = false ) { global $_wp_admin_css_colors; $fill = ( false !== $icon_color ) ? sanitize_hex_color( $icon_color ) : '#82878c'; if ( is_admin() && false === $icon_color && get_user_option( 'admin_color' ) ) { $admin_colors = json_decode( wp_json_encode( $_wp_admin_css_colors ), true ); $current_color = get_user_option( 'admin_color' ); $fill = $admin_colors[ $current_color ]['icon_colors']['base']; } // Flat $svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:' . $fill . '" viewBox="0 0 36.2 34.39" role="img" aria-hidden="true" focusable="false"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path fill="' . $fill . '" d="M24.41,0H4L0,18.39H12.16v2a2,2,0,0,0,4.08,0v-2H24.1a8.8,8.8,0,0,1,4.09-1Z"/><path fill="' . $fill . '" d="M21.5,20.4H18.24a4,4,0,0,1-8.08,0v0H.2v8.68H19.61a9.15,9.15,0,0,1-.41-2.68A9,9,0,0,1,21.5,20.4Z"/><path fill="' . $fill . '" d="M28.7,33.85a7,7,0,1,1,7-7A7,7,0,0,1,28.7,33.85Zm-1.61-5.36h5V25.28H30.31v-3H27.09Z"/><path fill="' . $fill . '" d="M28.7,20.46a6.43,6.43,0,1,1-6.43,6.43,6.43,6.43,0,0,1,6.43-6.43M26.56,29h6.09V24.74H30.84V21.8H26.56V29m2.14-9.64a7.5,7.5,0,1,0,7.5,7.5,7.51,7.51,0,0,0-7.5-7.5ZM27.63,28V22.87h2.14v2.95h1.81V28Z"/></g></g></svg>'; if ( $base64 ) { return 'data:image/svg+xml;base64,' . base64_encode( $svg ); } return $svg; } /** * Varnish Right Now Information * This information is put on the Dashboard 'Right now' widget * * @since 1.0 */ public function varnish_rightnow() { global $blog_id; // translators: %1$s links to the plugin's page on WordPress.org. $intro = sprintf( __( '<a href="%1$s">Proxy Cache Purge</a> automatically deletes your cached posts when published or updated. When making major site changes, such as with a new theme, plugins, or widgets, you may need to manually empty the cache.', 'varnish-http-purge' ), 'http://wordpress.org/plugins/varnish-http-purge/' ); $url = wp_nonce_url( add_query_arg( 'vhp_flush_do', 'all' ), 'vhp-flush-do' ); $button = __( 'Press the button below to force it to empty your entire cache.', 'varnish-http-purge' ); $button .= '</p><p><span class="button"><strong><a href="' . $url . '">'; $button .= __( 'Empty Cache', 'varnish-http-purge' ); $button .= '</a></strong></span>'; $nobutton = __( 'You do not have permission to empty the proxy cache for the whole site. Please contact your administrator.', 'varnish-http-purge' ); if ( // SingleSite - admins can always purge. ( ! is_multisite() && current_user_can( 'activate_plugins' ) ) || // Multisite - Network Admin can always purge. current_user_can( 'manage_network' ) || // Multisite - Site admins can purge UNLESS it's a subfolder install and we're on site #1. ( is_multisite() && current_user_can( 'activate_plugins' ) && ( SUBDOMAIN_INSTALL || ( ! SUBDOMAIN_INSTALL && ( BLOG_ID_CURRENT_SITE !== $blog_id ) ) ) ) ) { $text = $intro . ' ' . $button; } else { $text = $intro . ' ' . $nobutton; } // @codingStandardsIgnoreStart // This is safe to echo as it's controlled and secured above. // Using wp_kses will delete the icon. echo '<p class="varnish-rightnow">' . $text . '</p>'; // @codingStandardsIgnoreEnd } /** * Registered Events * These are when the purge is triggered * * @since 1.0 * @access protected */ protected function get_register_events() { // Define registered purge events. $actions = array( 'autoptimize_action_cachepurged', // Compat with https://wordpress.org/plugins/autoptimize/ plugin. 'delete_attachment', // Delete an attachment - includes re-uploading. 'deleted_post', // Delete a post. 'edit_post', // Edit a post - includes leaving comments. 'import_start', // When importer starts 'import_end', // When importer ends 'save_post', // Save a post. 'switch_theme', // After a theme is changed. 'customize_save_after', // After Customizer is updated. 'trashed_post', // Empty Trashed post. /* RAIDBOXES_START (IMPORT OF OLD NGINX CACHE PLUGIN) */ 'clean_post_cache', 'comment_post', 'delete_comment', 'delete_post', 'delete_term', 'edit_attachment', 'edit_comment', 'edit_term', 'edit_user_profile_update', 'pingback_post', 'publish_phone', 'trackback_post', 'transition_comment_status', 'transition_post_status', 'wp_insert_comment', 'wp_set_comment_status', 'wp_trash_post', 'wp_update_nav_menu', 'et_core_static_resources_removed',// Compat with Divi Theme. 'elementor/core/files/clear_cache',// Compat with Elementor plugin. /* RAIDBOXES_END */ ); // send back the actions array, filtered. // @param array $actions the actions that trigger the purge event. return apply_filters( 'varnish_http_purge_events', $actions ); } /** * Events that have no post IDs * These are when a full purge is triggered * * @since 3.9 * @access protected */ protected function get_no_id_events() { // Define registered purge events. $actions = array( 'autoptimize_action_cachepurged', // Compat with https://wordpress.org/plugins/autoptimize/ plugin. 'import_start', // When importer starts 'import_end', // When importer ends 'switch_theme', // After a theme is changed. 'customize_save_after', // After Customizer is updated. /* RAIDBOXES_START (IMPORT OF OLD NGINX CACHE PLUGIN) Only events that change something printed on every page. The rest of the nginx plugin's list is routed through get_event_routes(). */ 'delete_term', // Drops the term's link from every post that had it. 'edit_user_profile_update', // The display name is printed under every post. 'wp_update_nav_menu', // Menus are rendered site wide. 'et_core_static_resources_removed',// Compat with Divi Theme. 'elementor/core/files/clear_cache',// Compat with Elementor plugin. /* RAIDBOXES_END */ ); /** * Send back the actions array, filtered * * @param array $actions the actions that trigger the purge event * * DEVELOPERS! USE THIS SPARINGLY! YOU'RE A GREAT BIG 💩 IF YOU USE IT FLAGRANTLY * Remember to add your action to this AND varnish_http_purge_events due to shenanigans */ return apply_filters( 'varnish_http_purge_events_full', $actions ); } /* RAIDBOXES_START Skip purges during autosaves and Heartbeat requests. This used to take a $post_id and run wp_is_post_revision() on it, but most callers do not pass a post ID: execute_purge_no_id() is hooked to things like wp_update_nav_menu, where the first argument is a menu ID. A purge was dropped whenever such an ID collided with a revision's. The revision check now lives in purge_post(), the only caller that knows it has a post ID. */ protected function is_autosave_or_heartbeat() { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return true; } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'heartbeat', 'wp_autosave' ), true ) ) { return true; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST && isset( $GLOBALS['wp'] ) && isset( $GLOBALS['wp']->query_vars['rest_route'] ) && false !== strpos( $GLOBALS['wp']->query_vars['rest_route'], '/autosaves' ) ) { return true; } return false; } // Events that carry an object we can resolve to specific URLs, mapped to the // handler for their hook signature and the number of arguments it passes. // Events in get_no_id_events() never reach this; unrouted events fall back to // purge_post(). protected function get_event_routes() { $routes = array( // First argument is a post ID. 'clean_post_cache' => array( 'purge_post', 1 ), 'delete_post' => array( 'purge_post', 1 ), 'publish_phone' => array( 'purge_post', 1 ), 'wp_trash_post' => array( 'purge_post', 1 ), // Attachments sit at post status 'inherit', which purge_post() skips. 'delete_attachment' => array( 'purge_attachment', 1 ), 'edit_attachment' => array( 'purge_attachment', 1 ), // ( $new_status, $old_status, $object ). 'transition_comment_status' => array( 'purge_comment_transition', 3 ), 'transition_post_status' => array( 'purge_post_transition', 3 ), // First argument is a comment ID. 'comment_post' => array( 'purge_comment', 1 ), 'delete_comment' => array( 'purge_comment', 1 ), 'edit_comment' => array( 'purge_comment', 1 ), 'pingback_post' => array( 'purge_comment', 1 ), 'trackback_post' => array( 'purge_comment', 1 ), 'wp_insert_comment' => array( 'purge_comment', 1 ), 'wp_set_comment_status' => array( 'purge_comment', 1 ), // First argument is a term ID. 'edit_term' => array( 'purge_term', 3 ), ); return apply_filters( 'rb_cache_purge_event_routes', $routes ); } // 'trash' and 'future' are not publicly viewable, but a post moving in or out // of them was, or is about to be, so its URL still needs flushing. protected function is_public_post_status( $status ) { if ( in_array( $status, array( 'future', 'private', 'trash' ), true ) ) { return true; } $public_statuses = get_post_stati( array( 'public' => true ) ); return isset( $public_statuses[ $status ] ); } // get_permalink() returns the ?p=<id> form for a post that is not published, // which is the wrong URL to purge when one has just been unpublished. protected function the_public_permalink( $post ) { $post = get_post( $post ); if ( ! $post instanceof WP_Post ) { return false; } if ( ! in_array( $post->post_status, array( 'auto-draft', 'draft', 'future', 'pending' ), true ) ) { return get_permalink( $post ); } $published = clone $post; $published->post_status = 'publish'; return get_permalink( $published ); } // transition_post_status fires on every write to the posts table, including // autosaves, revisions and metadata-only saves where nothing moves. Upstream // registered it with one accepted argument, so it received $new_status where // it expected a post ID. public function purge_post_transition( $new_status, $old_status, $post ) { if ( ! $post instanceof WP_Post || $new_status === $old_status ) { return; } $was_public = $this->is_public_post_status( $old_status ); $is_public = $this->is_public_post_status( $new_status ); // Never public before and still not public: nothing is cached. if ( ! $was_public && ! $is_public ) { return; } // Leaving a public status is the one case where purge_post() has to ignore // the post's current status: the URL it just vacated is still cached. $this->purge_post( $post->ID, ! $is_public ); } // Attachments are stored at post status 'inherit', which purge_post() treats // as "nothing cached here", so they need their own URL and their parent. public function purge_attachment( $attachment_id ) { if ( $this->is_autosave_or_heartbeat() ) { return; } $attachment = get_post( $attachment_id ); if ( ! $attachment instanceof WP_Post ) { return; } $permalink = get_permalink( $attachment ); if ( $permalink ) { array_push( $this->purge_urls, $permalink ); } if ( ! empty( $attachment->post_parent ) ) { $this->purge_post( (int) $attachment->post_parent ); } } // Comment hooks pass a comment ID, not a post ID, so upstream could only flush // the whole site: approving one comment wiped the cache for the entire website. public function purge_comment( $comment_id ) { if ( $this->is_autosave_or_heartbeat() ) { return; } $comment = get_comment( $comment_id ); if ( ! $comment || empty( $comment->comment_post_ID ) ) { return; } $post_id = (int) $comment->comment_post_ID; $this->purge_post( $post_id ); // purge_post() only adds the comment feed for post type 'post'. $comment_feed = get_post_comments_feed_link( $post_id ); if ( $comment_feed ) { array_push( $this->purge_urls, $comment_feed ); } } public function purge_comment_transition( $new_status, $old_status, $comment ) { if ( $new_status === $old_status ) { return; } // Only approved comments are rendered, so spam -> trash changes nothing. if ( 'approved' !== $new_status && 'approved' !== $old_status ) { return; } if ( ! is_object( $comment ) || empty( $comment->comment_ID ) ) { return; } $this->purge_comment( $comment->comment_ID ); } public function remember_term_before_edit( $term_id, $taxonomy = '' ) { $term = get_term( $term_id, $taxonomy ); if ( ! $term || is_wp_error( $term ) ) { return; } $this->terms_before_edit[ (int) $term_id ] = array( 'name' => $term->name, 'slug' => $term->slug, ); } // A term's name and slug are printed wherever the term is linked, which with a // category widget is every page, so a rename really does need the whole cache. // An edit that only touches the description changes the archive and nothing else. public function purge_term( $term_id, $tt_id = 0, $taxonomy = '' ) { if ( $this->is_autosave_or_heartbeat() ) { return; } $term = get_term( $term_id, $taxonomy ); if ( ! $term || is_wp_error( $term ) ) { return; } // Private taxonomies have no archive anyone can have cached. $taxonomy_object = get_taxonomy( $term->taxonomy ); if ( ! $taxonomy_object || empty( $taxonomy_object->public ) ) { return; } $before = isset( $this->terms_before_edit[ (int) $term_id ] ) ? $this->terms_before_edit[ (int) $term_id ] : null; if ( null !== $before && ( $before['name'] !== $term->name || $before['slug'] !== $term->slug ) ) { $this->execute_purge_no_id( $term_id ); return; } $term_link = get_term_link( $term, $term->taxonomy ); if ( ! is_wp_error( $term_link ) ) { array_push( $this->purge_urls, $term_link, trailingslashit( $term_link ) . 'feed/' ); } // rest_base, not the taxonomy name: 'category' is served at /wp/v2/categories. if ( ! empty( $taxonomy_object->rest_base ) ) { array_push( $this->purge_urls, get_rest_url() . 'wp/v2/' . $taxonomy_object->rest_base . '/' . (int) $term_id . '/' ); } } /* RAIDBOXES_END */ /** * Execute Purge * Run the purge command for the URLs. Calls $this->purge_url for each URL * * @since 1.0 * @access protected */ public function execute_purge() { $purge_urls = array_unique( $this->purge_urls ); if ( ! empty( $purge_urls ) && is_array( $purge_urls ) ) { // If there are URLs to purge and it's an array, we'll likely purge. // Number of URLs to purge. $count = count( $purge_urls ); /* RAIDBOXES_START // Max posts if ( defined( 'VHP_VARNISH_MAXPOSTS' ) && false !== VHP_VARNISH_MAXPOSTS ) { $max_posts = VHP_VARNISH_MAXPOSTS; } else { $max_posts = get_site_option( 'vhp_varnish_max_posts_before_all' ); } RAIDBOXES_END */ /* RAIDBOXES_START - upstream's default of 50 is reached by an ordinary post: the permalink, the REST route, the feeds, the home page, the author archive and a URL for every category and tag. Collapsing to PURGE /.* there throws away the whole point of purging by URL. The list is sent in parallel batches, so its length costs the worker far less than its length in requests suggests. */ $max_posts = 500; /* RAIDBOXES_END */ // If there are more than vhp_varnish_max_posts_before_all URLs to purge (default 50), // do a purge ALL instead. Else, do the normal. if ( $max_posts <= $count ) { // Too many URLs, purge all instead. $this->purge_url( $this->the_home_url() . '/?vhp-regex' ); } else { /* RAIDBOXES_START - build the whole list first, then send it in one go, so the worker waits once instead of once per URL. */ $requests = array(); foreach ( $purge_urls as $url ) { $requests = array_merge( $requests, self::build_purge_requests( $url ) ); } self::send_purge_requests( $requests ); /* RAIDBOXES_END */ } } elseif ( isset( $_GET ) ) { // Otherwise, if we've passed a GET call... if ( isset( $_GET['vhp_flush_all'] ) && check_admin_referer( 'vhp-flush-all' ) ) { // Flush Cache recursive. $this->purge_url( $this->the_home_url() . '/?vhp-regex' ); } elseif ( isset( $_GET['vhp_flush_do'] ) && check_admin_referer( 'vhp-flush-do' ) ) { if ( 'object' === $_GET['vhp_flush_do'] ) { // Flush Object Cache (with a double check). if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) { wp_cache_flush(); } } elseif ( 'all' === $_GET['vhp_flush_do'] ) { // Flush Cache recursive. $this->purge_url( $this->the_home_url() . '/?vhp-regex' ); } else { // Flush the URL we're on. $p = wp_parse_url( esc_url_raw( wp_unslash( $_GET['vhp_flush_do'] ) ) ); if ( ! isset( $p['host'] ) ) { return; } $this->purge_url( esc_url_raw( wp_unslash( $_GET['vhp_flush_do'] ) ) ); } } } } /** * Purge URL * Parse the URL for proxy proxies * * @since 1.0 * @param array $url - The url to be purged. * @access protected */ /* RAIDBOXES_START - split in two. Building the request list is separate from sending it, so execute_purge() can hand the whole list to curl_multi in one batch instead of waiting on one request after another. */ public static function purge_url( $url ) { self::send_purge_requests( self::build_purge_requests( $url ) ); } // The PURGE requests for one URL, one per host, each with the URL to hit and // the headers to send with it. protected static function build_purge_requests( $url ) { $requests = array(); /* RAIDBOXES_END */ // Bail early if someone sent a non-URL if ( false === filter_var( $url, FILTER_VALIDATE_URL ) ) { return $requests; } $p = wp_parse_url( $url ); // Bail early if there's no host since some plugins are weird. if ( ! isset( $p['host'] ) ) { return $requests; } // Determine if we're using regex to flush all pages or not. $pregex = ''; $x_purge_method = 'default'; if ( isset( $p['query'] ) && ( 'vhp-regex' === $p['query'] ) ) { $pregex = '.*'; $x_purge_method = 'regex'; } // Build a varniship to sail. ⛵️ /* RAIDBOXES_START $varniship = ( VHP_VARNISH_IP !== false ) ? VHP_VARNISH_IP : get_site_option( 'vhp_varnish_ip' ); // If there are commas, and for whatever reason this didn't become an array // properly, force it. if ( ! is_array( $varniship ) && strpos( $varniship, ',' ) !== false ) { $varniship = array_map( 'trim', explode( ',', $varniship ) ); } // Now apply filters if ( is_array( $varniship ) ) { // To each ship: for ( $i = 0; $i++; $i < count( $varniship ) ) { $varniship[ $i ] = apply_filters( 'vhp_varnish_ip', $varniship[ $i ] ); } } else { // To the only ship: $varniship = apply_filters( 'vhp_varnish_ip', $varniship ); } RAIDBOXES_END */ // Determine the path. $path = ( isset( $p['path'] ) ) ? $p['path'] : ''; /** * Schema filter * * Allows default http:// schema to be changed to https * varnish_http_purge_schema() * * @since 3.7.3 */ // This is a very annoying check for DreamHost who needs to default to HTTPS without breaking // people who've been around before. /* RAIDBOXES_START $server_hostname = gethostname(); switch ( substr( $server_hostname, 0, 3 ) ) { case 'dp-': $schema_type = 'https://'; break; default: $schema_type = 'http://'; break; } $schema = apply_filters( 'varnish_http_purge_schema', $schema_type ); // When we have Varnish IPs, we use them in lieu of hosts. if ( isset( $varniship ) && ! empty( $varniship ) ) { $all_hosts = ( ! is_array( $varniship ) ) ? array( $varniship ) : $varniship; } else { // The default is the main host, converted into an array. $all_hosts = array( $p['host'] ); } RAIDBOXES_END */ /* RAIDBOXES_START */ $schema = 'https://'; $all_hosts = array( RB_CACHE_DOMAIN ); // Since the ship is always an array now, let's loop. foreach ( $all_hosts as $one_host ) { /** * Allow setting of ports in host name * Credit: davidbarratt - https://github.com/Ipstenu/varnish-http-purge/pull/38/ * * (default value: $p['host']) * * @var string * @access public * @since 4.4.0 */ /* RAIDBOXES_START $host_headers = $p['host']; RAIDBOXES_END */ /* RAIDBOXES_START */ $host_headers = RB_CACHE_DOMAIN; /* RAIDBOXES_END */ // If the URL to be purged has a port, we're going to re-use it. if ( isset( $p['port'] ) ) { $host_headers .= ':' . $p['port']; } $parsed_url = $url; // Filter URL based on the Proxy IP for nginx compatibility if ( 'localhost' === $one_host ) { $parsed_url = str_replace( $p['host'], 'localhost', $parsed_url ); } // Collapse repeated slashes. Not rtrim( $path, '/' ) . '/': that also added // a slash to /foo, and a plain PURGE matches obj.http.x-url exactly. $path = ( '' === $path ) ? '/' : preg_replace( '#/{2,}#', '/', $path ); // Create path to purge. $purgeme = $schema . $one_host . $path . $pregex; // Check the queries... if ( ! empty( $p['query'] ) && 'vhp-regex' !== $p['query'] ) { $purgeme .= '?' . $p['query']; } /** * Filter the purge path * * Allows dynamically changing the purge cache for custom purge location * or systems not supporting .* regex purge for example * * @since 5.1 */ $purgeme = apply_filters( 'vhp_purgeme_path', $purgeme, $schema, $one_host, $path, $pregex, $p ); /** * Filters the HTTP headers to send with a PURGE request. * * @since 4.1 */ $headers = apply_filters( 'varnish_http_purge_headers', array( 'host' => $host_headers, 'X-Purge-Method' => $x_purge_method, ) ); /* RAIDBOXES_START - collected, not sent. send_purge_requests() below. */ $requests[] = array( 'url' => $purgeme, 'headers' => $headers, 'parsed_url' => $parsed_url, ); /* RAIDBOXES_END */ } /* RAIDBOXES_START */ return $requests; } /* Purges run on shutdown, so every one of them is time the PHP-FPM worker spends not serving anyone. wp_remote_request() cannot be made to stop waiting. It goes through the Requests library, which prefers the cURL transport, and that transport calls curl_exec() synchronously under CURLOPT_TIMEOUT whatever 'blocking' is set to; 'blocking' only decides whether the response is captured. Fire and forget happens on the Fsockopen transport, which is the fallback, not what our boxes use. Sending the list one at a time therefore costs the worker the sum of every purge in it, which is worse than the single full purge this replaces. curl_multi sends them together, so a batch costs about what its slowest request costs, and the connection to the host is reused instead of a fresh TLS handshake per URL. Only PURGE_BATCH_SIZE are in flight at a time, so a post with a lot of terms does not open a hundred connections to our own front door at once. */ const PURGE_TIMEOUT = 2; const PURGE_BATCH_SIZE = 12; protected static function send_purge_requests( $requests ) { if ( empty( $requests ) ) { return; } // curl_multi is optional in a PHP build. Fall back to one at a time // rather than dropping the purge. if ( ! function_exists( 'curl_multi_init' ) ) { foreach ( $requests as $request ) { self::send_purge_request( $request ); } return; } foreach ( array_chunk( $requests, self::PURGE_BATCH_SIZE ) as $batch ) { self::send_purge_batch( $batch ); } } // One curl_multi run. protected static function send_purge_batch( $batch ) { $multi = curl_multi_init(); $handles = array(); foreach ( $batch as $index => $request ) { $header_lines = array(); foreach ( $request['headers'] as $name => $value ) { $header_lines[] = $name . ': ' . $value; } $handle = curl_init(); curl_setopt( $handle, CURLOPT_URL, $request['url'] ); curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PURGE' ); curl_setopt( $handle, CURLOPT_HTTPHEADER, $header_lines ); curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); // Verification is off for the same reason it was off on the // wp_remote_request() call: Varnish itself speaks HTTP, and the // certificate on the way to it is not ours to check. curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, false ); curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, 0 ); curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, self::PURGE_TIMEOUT ); curl_setopt( $handle, CURLOPT_TIMEOUT, self::PURGE_TIMEOUT ); curl_multi_add_handle( $multi, $handle ); $handles[ $index ] = $handle; } // Bounded by CURLOPT_TIMEOUT on every handle, so this cannot sit here // longer than PURGE_TIMEOUT. do { $status = curl_multi_exec( $multi, $running ); if ( $running ) { // select() returns -1 with no descriptors to wait on. Sleeping // briefly keeps that from becoming a busy loop. if ( -1 === curl_multi_select( $multi, 0.1 ) ) { usleep( 1000 ); } } } while ( $running && CURLM_OK === $status ); foreach ( $handles as $index => $handle ) { $error = curl_error( $handle ); if ( '' !== $error ) { $response = new WP_Error( 'http_request_failed', $error ); } else { $response = array( 'body' => (string) curl_multi_getcontent( $handle ), 'response' => array( 'code' => (int) curl_getinfo( $handle, CURLINFO_HTTP_CODE ), 'message' => '', ), ); } self::after_purge( $batch[ $index ], $response ); curl_multi_remove_handle( $multi, $handle ); curl_close( $handle ); } curl_multi_close( $multi ); } // No curl_multi in this build, so one blocking request, as it used to be. protected static function send_purge_request( $request ) { $response = wp_remote_request( $request['url'], array( 'sslverify' => false, 'method' => 'PURGE', 'headers' => $request['headers'], 'timeout' => self::PURGE_TIMEOUT, ) ); self::after_purge( $request, $response ); } protected static function after_purge( $request, $response ) { if ( WP_DEBUG === true ) { if ( is_wp_error( $response ) ) { error_log( '[RB-CACHE] Response for request (PURGE ' . $request['url'] . ') failed: ' . $response->get_error_message() ); } else { error_log( '[RB-CACHE] Response for request (PURGE ' . $request['url'] . ') received: ' . $response['body'] . ' (' . $response['response']['code'] . ') ' ); } } do_action( 'after_purge_url', $request['parsed_url'], $request['url'], $response, $request['headers'] ); } /* RAIDBOXES_END */ /** * Purge - No IDs * Flush the whole cache * * @access public * @param mixed $post_id - the post ID that triggered this (we don't use it yet). * @return void * @since 3.9 */ public function execute_purge_no_id( $post_id = 0 ) { /* RAIDBOXES_START - $post_id is not inspected: the events still routed here pass a menu, user or term ID. */ if ( $this->is_autosave_or_heartbeat() ) { return; } /* RAIDBOXES_END */ $listofurls = array(); array_push( $listofurls, $this->the_home_url() . '/?vhp-regex' ); // Now flush all the URLs we've collected provided the array isn't empty. if ( ! empty( $listofurls ) ) { foreach ( $listofurls as $url ) { array_push( $this->purge_urls, $url ); } } do_action( 'after_full_purge' ); } /** * Generate URLs * * Generates a list of URLs that should be purged, based on the post ID * passed through. Useful for when you're trying to get a post to flush * another post. * * @access public * @param mixed $post_id - The ID of the post to be purged. * @return array() */ public function generate_urls( $post_id ) { /* RAIDBOXES_START - bypass the memo, callers expect a list back. */ $this->purge_post( $post_id, true ); /* RAIDBOXES_END */ return $this->purge_urls; } /** * Purge Post * Flush the post * * @since 1.0 * @param array $post_id - The ID of the post to be purged. * @access public */ public function purge_post( $post_id, $force = false ) { /* RAIDBOXES_START */ if ( $this->is_autosave_or_heartbeat() || wp_is_post_revision( $post_id ) ) { return; } if ( ! $force && isset( $this->purged_post_ids[ (int) $post_id ] ) ) { return; } /* RAIDBOXES_END */ /** * If this is a valid post we want to purge the post, * the home page and any associated tags and categories */ /* RAIDBOXES_START - 'pending' and 'draft' dropped: they have no public URL, so purging one only flushed the home page, the feeds and every archive it might one day appear on. Un-publishing arrives via purge_post_transition() with $force set. */ $valid_post_status = array( 'publish', 'private', 'trash' ); /* RAIDBOXES_END */ $this_post_status = get_post_status( $post_id ); // Not all post types are created equal. $invalid_post_type = array( 'nav_menu_item', 'revision' ); $noarchive_post_type = array( 'post', 'page' ); $this_post_type = get_post_type( $post_id ); /** * Determine the route for the rest API * This will need to be revisited if WP updates the version. * Future me: Consider an array? 4.7-?? use v2, and then adapt from there? */ if ( version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) { $json_disabled = false; $json_disablers = array( 'disable-json-api/disable-json-api.php', ); foreach ( $json_disablers as $json_plugin ) { if ( function_exists( 'is_plugin_active' ) && is_plugin_active( $json_plugin ) ) { $json_disabled = true; } } // If json is NOT disabled... if ( ! $json_disabled ) { $rest_api_route = 'wp/v2'; } } // array to collect all our URLs. $listofurls = array(); // Verify we have a permalink and that we're a valid post status and type. /* RAIDBOXES_START - added $force, and the_public_permalink() so that un-publishing flushes the pretty URL rather than the ?p=<id> form. */ if ( false !== get_permalink( $post_id ) && ( $force || in_array( $this_post_status, $valid_post_status, true ) ) && ! in_array( $this_post_type, $invalid_post_type, true ) ) { // Marked only once we know there was work to do, so a call that bailed // on the status does not shadow a later forced one. $this->purged_post_ids[ (int) $post_id ] = true; // Post URL. array_push( $listofurls, $this->the_public_permalink( $post_id ) ); /* RAIDBOXES_END */ /** * JSON API Permalink for the post based on type * We only want to do this if the rest_base exists * But we apparently have to force it for posts and pages (seriously?) */ if ( isset( $rest_api_route ) ) { $post_type_object = get_post_type_object( $post_id ); $rest_permalink = false; if ( isset( $post_type_object->rest_base ) ) { $rest_permalink = get_rest_url() . $rest_api_route . '/' . $post_type_object->rest_base . '/' . $post_id . '/'; } elseif ( 'post' === $this_post_type ) { $rest_permalink = get_rest_url() . $rest_api_route . '/posts/' . $post_id . '/'; } elseif ( 'page' === $this_post_type ) { $rest_permalink = get_rest_url() . $rest_api_route . '/pages/' . $post_id . '/'; } if ( isset( $rest_permalink ) ) { array_push( $listofurls, $rest_permalink ); } } // Add in AMP permalink for official WP AMP plugin: // https://wordpress.org/plugins/amp/ if ( function_exists( 'amp_get_permalink' ) ) { array_push( $listofurls, amp_get_permalink( $post_id ) ); } // Regular AMP url for posts if ant of the following are active: // https://wordpress.org/plugins/accelerated-mobile-pages/ if ( defined( 'AMPFORWP_AMP_QUERY_VAR' ) ) { array_push( $listofurls, get_permalink( $post_id ) . 'amp/' ); } // Also clean URL for trashed post. if ( 'trash' === $this_post_status ) { $trashpost = get_permalink( $post_id ); $trashpost = str_replace( '__trashed', '', $trashpost ); array_push( $listofurls, $trashpost, $trashpost . 'feed/' ); } // Category purge based on Donnacha's work in WP Super Cache. $categories = get_the_category( $post_id ); if ( $categories ) { foreach ( $categories as $cat ) { array_push( $listofurls, get_category_link( $cat->term_id ), get_rest_url() . $rest_api_route . '/categories/' . $cat->term_id . '/' ); } } // Tag purge based on Donnacha's work in WP Super Cache. $tags = get_the_tags( $post_id ); if ( $tags ) { $tag_base = get_site_option( 'tag_base' ); if ( '' === $tag_base ) { $tag_base = '/tag/'; } foreach ( $tags as $tag ) { array_push( $listofurls, get_tag_link( $tag->term_id ), get_rest_url() . $rest_api_route . $tag_base . $tag->term_id . '/' ); } } // Custom Taxonomies: Only show if the taxonomy is public. $taxonomies = get_post_taxonomies( $post_id ); if ( $taxonomies ) { foreach ( $taxonomies as $taxonomy ) { $features = (array) get_taxonomy( $taxonomy ); if ( $features['public'] ) { $terms = wp_get_post_terms( $post_id, $taxonomy ); /* RAIDBOXES_START - the REST route upstream built here, /wp/v2/<taxonomy>/<slug>/, does not exist. Terms are served at the taxonomy's rest_base and are addressed by ID, so the purge matched nothing and only used up the URL budget. */ foreach ( $terms as $term ) { array_push( $listofurls, get_term_link( $term ) ); } /* RAIDBOXES_END */ } } } // If the post is a post, we have more things to flush // Pages and Woo Things don't need all this. if ( $this_post_type && 'post' === $this_post_type ) { // Author URLs: $author_id = get_post_field( 'post_author', $post_id ); array_push( $listofurls, get_author_posts_url( $author_id ), get_author_feed_link( $author_id ), get_rest_url() . $rest_api_route . '/users/' . $author_id . '/' ); // Feeds: array_push( $listofurls, get_bloginfo_rss( 'rdf_url' ), get_bloginfo_rss( 'rss_url' ), get_bloginfo_rss( 'rss2_url' ), get_bloginfo_rss( 'atom_url' ), get_bloginfo_rss( 'comments_rss2_url' ), get_post_comments_feed_link( $post_id ) ); } // Archives and their feeds. if ( $this_post_type && ! in_array( $this_post_type, $noarchive_post_type, true ) ) { array_push( $listofurls, get_post_type_archive_link( get_post_type( $post_id ) ), get_post_type_archive_feed_link( get_post_type( $post_id ) ) // Need to add in JSON? ); } // Home Pages and (if used) posts page. array_push( $listofurls, get_rest_url(), $this->the_home_url() . '/' ); if ( 'page' === get_site_option( 'show_on_front' ) ) { // Ensure we have a page_for_posts setting to avoid empty URL. if ( get_site_option( 'page_for_posts' ) ) { array_push( $listofurls, get_permalink( get_site_option( 'page_for_posts' ) ) ); } } } else { // We're not sure how we got here, but bail instead of processing anything else. return; } // If the array isn't empty, proceed. if ( empty( $listofurls ) ) { return; } else { // Strip off query variables foreach ( $listofurls as $url ) { $url = strtok( $url, '?' ); } // If the DOMAINS setup is defined, we duplicate the URLs /* RAIDBOXES_START if ( false !== false ) { // Split domains into an array $domains = explode( ',', VHP_DOMAINS ); $newurls = array(); // Loop through all the domains foreach ( $domains as $a_domain ) { foreach ( $listofurls as $url ) { // If the URL contains the filtered home_url, and is NOT equal to the domain we're trying to replace, we will add it to the new urls if ( false !== strpos( $this->the_home_url(), $url ) && $this->the_home_url() !== $a_domain ) { $newurls[] = str_replace( $this->the_home_url(), $a_domain, $url ); } // If the URL contains the raw home_url, and is NOT equal to the domain we're trying to replace, we will add it to the new urls if ( false !== strpos( home_url(), $url ) && home_url() !== $a_domain ) { $newurls[] = str_replace( home_url(), $a_domain, $url ); } } } // Merge all the URLs array_push( $listofurls, $newurls ); } RAIDBOXES_END */ // Make sure each URL only gets purged once, eh? $purgeurls = array_unique( $listofurls, SORT_REGULAR ); // Flush all the URLs foreach ( $purgeurls as $url ) { array_push( $this->purge_urls, $url ); } } /* * Filter to add or remove urls to the array of purged urls * @param array $purge_urls the urls (paths) to be purged * @param int $post_id the id of the new/edited post */ $this->purge_urls = apply_filters( 'vhp_purge_urls', $this->purge_urls, $post_id ); } // @codingStandardsIgnoreStart /* * These have all been name changed to proper names, but just in case... */ public function getRegisterEvents() { self::get_register_events(); } public function getNoIDEvents() { self::get_no_id_events(); } public function executePurge() { self::execute_purge(); } public function purgeNoID( $post_id ) { self::execute_purge_no_id( $post_id ); } public function purgeURL( $url ) { self::purge_url( $url ); } public function purgePost( $post_id ) { self::purge_post( $post_id ); } // @codingStandardsIgnoreEnd } /** * Purge via WP-CLI * * @since 3.8 */ if ( defined( 'WP_CLI' ) && WP_CLI ) { include 'wp-cli.php'; } /* RAIDBOXES_START // Preventing people from forking this and hurting themselve by having two versions, though it may not work. if ( ! class_exists( 'VarnishStatus' ) ) { RAIDBOXES_END */ /* * Settings Pages * * @since 4.0 */ // The settings PAGES aren't needed on the network admin page /* RAIDBOXES_START if ( ! is_network_admin() ) { require_once 'settings.php'; } require_once 'debug.php'; require_once 'health-check.php'; $purger = new VarnishPurger(); } RAIDBOXES_END */ /* RAIDBOXES_START */ new RBCachePlugin(); /* RAIDBOXES_END */