Advertisement
cnxsoft

Quick Cache Advanced Cache

Jun 29th, 2014
374
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2. /**
  3.  * Quick Cache (Advanced Cache Handler)
  4.  *
  5.  * This file serves as a template for the Quick Cache plugin in WordPress.
  6.  * The Quick Cache plugin will fill the `%%` replacement codes automatically.
  7.  *    This file becomes: `/wp-content/advanced-cache.php`.
  8.  *
  9.  * @package quick_cache\advanced_cache
  10.  * @since 140422 First documented version.
  11.  * @copyright WebSharks, Inc. <http://www.websharks-inc.com>
  12.  * @license GNU General Public License, version 2
  13.  */
  14. namespace quick_cache
  15. {
  16.     if(!defined('WPINC')) // MUST have WordPress.
  17.         exit('Do NOT access this file directly: '.basename(__FILE__));
  18.  
  19.     /**
  20.      * Quick Cache Pro flag.
  21.      *
  22.      * @since 140422 First documented version.
  23.      *
  24.      * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  25.      */
  26.     define('QUICK_CACHE_PRO', TRUE); // Note that we do NOT check `if(defined())` here.
  27.  
  28.     if(!defined('QUICK_CACHE_ENABLE'))
  29.         /**
  30.          * Is Quick Cache enabled?
  31.          *
  32.          * @since 140422 First documented version.
  33.          *
  34.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  35.          */
  36.         define('QUICK_CACHE_ENABLE', '1');
  37.  
  38.     if(!defined('QUICK_CACHE_DEBUGGING_ENABLE'))
  39.         /**
  40.          * Is Quick Cache debugging enabled?
  41.          *
  42.          * @since 140422 First documented version.
  43.          *
  44.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  45.          */
  46.         define('QUICK_CACHE_DEBUGGING_ENABLE', '1');
  47.  
  48.     if(!defined('QUICK_CACHE_ALLOW_BROWSER_CACHE'))
  49.         /**
  50.          * Allow browsers to cache each document?
  51.          *
  52.          * @since 140422 First documented version.
  53.          *
  54.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  55.          *
  56.          * @note If this is a `FALSE` (or an empty) value; Quick Cache will send no-cache headers.
  57.          *    If `TRUE`, Quick Cache will NOT send no-cache headers.
  58.          */
  59.         define('QUICK_CACHE_ALLOW_BROWSER_CACHE', '0');
  60.  
  61.     if(!defined('QUICK_CACHE_GET_REQUESTS'))
  62.         /**
  63.          * Cache `$_GET` requests w/ a query string?
  64.          *
  65.          * @since 140422 First documented version.
  66.          *
  67.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  68.          */
  69.         define('QUICK_CACHE_GET_REQUESTS', '0');
  70.  
  71.     if(!defined('QUICK_CACHE_CACHE_404_REQUESTS'))
  72.         /**
  73.          * Cache 404 errors?
  74.          *
  75.          * @since 140422 First documented version.
  76.          *
  77.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  78.          */
  79.         define('QUICK_CACHE_CACHE_404_REQUESTS', '0');
  80.  
  81.     if(!defined('QUICK_CACHE_FEEDS_ENABLE'))
  82.         /**
  83.          * Cache XML/RSS/Atom feeds?
  84.          *
  85.          * @since 140422 First documented version.
  86.          *
  87.          * @var string|integer|boolean A boolean-ish value; e.g. `1` or `0`.
  88.          */
  89.         define('QUICK_CACHE_FEEDS_ENABLE', '0');
  90.  
  91.     if(!defined('QUICK_CACHE_DIR'))
  92.         /**
  93.          * Directory used to store cache files; relative to `ABSPATH`.
  94.          *
  95.          * @since 140422 First documented version.
  96.          *
  97.          * @var string Absolute server directory path.
  98.          */
  99.         define('QUICK_CACHE_DIR', ABSPATH.'wp-content/cache/quick-cache/cache');
  100.  
  101.     if(!defined('QUICK_CACHE_MAX_AGE'))
  102.         /**
  103.          * Cache expiration time.
  104.          *
  105.          * @since 140422 First documented version.
  106.          *
  107.          * @var string Anything compatible with PHP's {@link \strtotime()}.
  108.          */
  109.         define('QUICK_CACHE_MAX_AGE', '7 days');
  110.  
  111.     if(!defined('QUICK_CACHE_404_CACHE_FILENAME'))
  112.         /**
  113.          * 404 file name (if applicable).
  114.          *
  115.          * @since 140422 First documented version.
  116.          *
  117.          * @var string A unique file name that will not conflict with real paths.
  118.          *    This should NOT include the extension; basename only please.
  119.          */
  120.         define('QUICK_CACHE_404_CACHE_FILENAME', '----404----');
  121.  
  122.     /**
  123.      * Quick Cache (Advanced Cache Handler)
  124.      *
  125.      * @package quick_cache\advanced_cache
  126.      * @since 140422 First documented version.
  127.      */
  128.     class advanced_cache # `/wp-content/advanced-cache.php`
  129.     {
  130.         /**
  131.          * Identifies the pro version of Quick Cache.
  132.          *
  133.          * @since 140422 First documented version.
  134.          *
  135.          * @var boolean `TRUE` for Quick Cache Pro; else `FALSE`.
  136.          */
  137.         public $is_pro = FALSE;
  138.  
  139.         /**
  140.          * Flagged as `TRUE` if QC advanced cache is active & running.
  141.          *
  142.          * @since 140605 Improving output buffers
  143.          *
  144.          * @var boolean `TRUE` if QC advanced cache is active & running.
  145.          */
  146.         public $is_running = FALSE;
  147.  
  148.         /**
  149.          * Microtime; defined by class constructor for debugging purposes.
  150.          *
  151.          * @since 140422 First documented version.
  152.          *
  153.          * @var float Result of a call to {@link \microtime()}.
  154.          */
  155.         public $timer = 0;
  156.  
  157.         /**
  158.          * Calculated protocol; one of `http://` or `https://`.
  159.          *
  160.          * @since 140422 First documented version.
  161.          *
  162.          * @var float One of `http://` or `https://`.
  163.          */
  164.         public $protocol = '';
  165.  
  166.         /**
  167.          * Calculated version salt; set by site configuration data.
  168.          *
  169.          * @since 140422 First documented version.
  170.          *
  171.          * @var string|mixed Any scalar value does fine.
  172.          */
  173.         public $version_salt = '';
  174.  
  175.         /**
  176.          * Calculated cache path for the current request;
  177.          *    absolute relative (no leading/trailing slashes).
  178.          *
  179.          * @since 140422 First documented version.
  180.          *
  181.          * @var string Absolute relative (no leading/trailing slashes).
  182.          *    Defined by {@link maybe_start_output_buffering()}.
  183.          */
  184.         public $cache_path = '';
  185.  
  186.         /**
  187.          * Calculated cache file location for the current request; absolute path.
  188.          *
  189.          * @since 140422 First documented version.
  190.          *
  191.          * @var string Cache file location for the current request; absolute path.
  192.          *    Defined by {@link maybe_start_output_buffering()}.
  193.          */
  194.         public $cache_file = '';
  195.  
  196.         /**
  197.          * Centralized 404 cache file location; absolute path.
  198.          *
  199.          * @since 140422 First documented version.
  200.          *
  201.          * @var string Centralized 404 cache file location; absolute path.
  202.          *    Defined by {@link maybe_start_output_buffering()}.
  203.          */
  204.         public $cache_file_404 = '';
  205.  
  206.         /**
  207.          * A possible version salt (string value); followed by the current request location.
  208.          *
  209.          * @since 140422 First documented version.
  210.          *
  211.          * @var string Version salt (string value); followed by the current request location.
  212.          *    Defined by {@link maybe_start_output_buffering()}.
  213.          */
  214.         public $salt_location = '';
  215.  
  216.         /**
  217.          * Array of data targeted at the postload phase.
  218.          *
  219.          * @since 140422 First documented version.
  220.          *
  221.          * @var array Data and/or flags that work with various postload handlers.
  222.          */
  223.         public $postload = array(
  224.             'filter_status_header' => TRUE, 'wp_main_query' => TRUE,
  225.             'set_debug_info'       => QUICK_CACHE_DEBUGGING_ENABLE,
  226.         );
  227.  
  228.         /**
  229.          * Last HTTP status code passed through {@link \status_header}.
  230.          *
  231.          * @since 140422 First documented version.
  232.          *
  233.          * @var null|integer Last HTTP status code (if applicable).
  234.          *
  235.          * @see maybe_filter_status_header_postload()
  236.          */
  237.         public $http_status;
  238.  
  239.         /**
  240.          * An array of debug info.
  241.          *
  242.          * @since 140605 Improve output buffering.
  243.          *
  244.          * @var array An array of debug info; i.e. `reason_code` and `reason` (optional).
  245.          */
  246.         public $debug_info = array('reason_code' => '', 'reason' => '');
  247.  
  248.         /**
  249.          * Have we caught the main WP loaded being loaded yet?
  250.          *
  251.          * @since 140422 First documented version.
  252.          *
  253.          * @var boolean `TRUE` if main query has been loaded; else `FALSE`.
  254.          *
  255.          * @see wp_main_query_postload()
  256.          */
  257.         public $is_wp_loaded_query = FALSE;
  258.  
  259.         /**
  260.          * Is the current request a WordPress 404 error?
  261.          *
  262.          * @since 140422 First documented version.
  263.          *
  264.          * @var boolean `TRUE` if is a 404 error; else `FALSE`.
  265.          *
  266.          * @see wp_main_query_postload()
  267.          */
  268.         public $is_404 = FALSE;
  269.  
  270.         /**
  271.          * Is the current request a WordPress content type?
  272.          *
  273.          * @since 140605 Improving debug notes display.
  274.          *
  275.          * @var boolean `TRUE` if is a WP content type.
  276.          *
  277.          * @see wp_main_query_postload()
  278.          */
  279.         public $is_a_wp_content_type = FALSE;
  280.  
  281.         /**
  282.          * Current WordPress {@link \site_url()}.
  283.          *
  284.          * @since 140422 First documented version.
  285.          *
  286.          * @var string Current WordPress {@link \site_url()}.
  287.          *
  288.          * @see wp_main_query_postload()
  289.          */
  290.         public $site_url = '';
  291.  
  292.         /**
  293.          * Current WordPress {@link \home_url()}.
  294.          *
  295.          * @since 140422 First documented version.
  296.          *
  297.          * @var string Current WordPress {@link \home_url()}.
  298.          *
  299.          * @see wp_main_query_postload()
  300.          */
  301.         public $home_url = '';
  302.  
  303.         /**
  304.          * Flag for {@link \is_user_loged_in()}.
  305.          *
  306.          * @since 140422 First documented version.
  307.          *
  308.          * @var boolean `TRUE` if {@link \is_user_loged_in()}; else `FALSE`.
  309.          *
  310.          * @see wp_main_query_postload()
  311.          */
  312.         public $is_user_logged_in = FALSE;
  313.  
  314.         /**
  315.          * Flag for {@link \is_maintenance()}.
  316.          *
  317.          * @since 140422 First documented version.
  318.          *
  319.          * @var boolean `TRUE` if {@link \is_maintenance()}; else `FALSE`.
  320.          *
  321.          * @see wp_main_query_postload()
  322.          */
  323.         public $is_maintenance = FALSE;
  324.  
  325.         /**
  326.          * Value for {@link plugin::$file()}.
  327.          *
  328.          * @since 140422 First documented version.
  329.          *
  330.          * @var string The value of {@link plugin::$file()}.
  331.          *
  332.          * @see wp_main_query_postload()
  333.          */
  334.         public $plugin_file = '';
  335.  
  336.         /**
  337.          * Text domain for translations; based on `__NAMESPACE__`.
  338.          *
  339.          * @since 140422 First documented version.
  340.          *
  341.          * @var string Defined by class constructor; for translations.
  342.          */
  343.         public $text_domain = '';
  344.  
  345.         /**
  346.          * Array of hooks added by plugins.
  347.          *
  348.          * @since 140422 First documented version.
  349.          *
  350.          * @var array An array of any hooks added by plugins.
  351.          */
  352.         public $hooks = array();
  353.  
  354.         /**
  355.          * No-cache because of the current {@link \PHP_SAPI}.
  356.          *
  357.          * @since 140422 First documented version.
  358.          *
  359.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  360.          */
  361.         const NC_DEBUG_PHP_SAPI_CLI = 'nc_debug_php_sapi_cli';
  362.  
  363.         /**
  364.          * No-cache because the current request includes the `?qcAC=0` parameter.
  365.          *
  366.          * @since 140422 First documented version.
  367.          *
  368.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  369.          */
  370.         const NC_DEBUG_QCAC_GET_VAR = 'nc_debug_qcac_get_var';
  371.  
  372.         /**
  373.          * No-cache because of a missing `$_SERVER['HTTP_HOST']`.
  374.          *
  375.          * @since 140422 First documented version.
  376.          *
  377.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  378.          */
  379.         const NC_DEBUG_NO_SERVER_HTTP_HOST = 'nc_debug_no_server_http_host';
  380.  
  381.         /**
  382.          * No-cache because of a missing `$_SERVER['REQUEST_URI']`.
  383.          *
  384.          * @since 140422 First documented version.
  385.          *
  386.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  387.          */
  388.         const NC_DEBUG_NO_SERVER_REQUEST_URI = 'nc_debug_no_server_request_uri';
  389.  
  390.         /**
  391.          * No-cache because the {@link \QUICK_CACHE_ALLOWED} constant says not to.
  392.          *
  393.          * @since 140422 First documented version.
  394.          *
  395.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  396.          */
  397.         const NC_DEBUG_QUICK_CACHE_ALLOWED_CONSTANT = 'nc_debug_quick_cache_allowed_constant';
  398.  
  399.         /**
  400.          * No-cache because the `$_SERVER['QUICK_CACHE_ALLOWED']` environment variable says not to.
  401.          *
  402.          * @since 140422 First documented version.
  403.          *
  404.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  405.          */
  406.         const NC_DEBUG_QUICK_CACHE_ALLOWED_SERVER_VAR = 'nc_debug_quick_cache_allowed_server_var';
  407.  
  408.         /**
  409.          * No-cache because the {@link \DONOTCACHEPAGE} constant says not to.
  410.          *
  411.          * @since 140422 First documented version.
  412.          *
  413.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  414.          */
  415.         const NC_DEBUG_DONOTCACHEPAGE_CONSTANT = 'nc_debug_donotcachepage_constant';
  416.  
  417.         /**
  418.          * No-cache because the `$_SERVER['DONOTCACHEPAGE']` environment variable says not to.
  419.          *
  420.          * @since 140422 First documented version.
  421.          *
  422.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  423.          */
  424.         const NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR = 'nc_debug_donotcachepage_server_var';
  425.  
  426.         /**
  427.          * No-cache because the current request method is `POST|PUT|DELETE`.
  428.          *
  429.          * @since 140422 First documented version.
  430.          *
  431.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  432.          */
  433.         const NC_DEBUG_POST_PUT_DEL_REQUEST = 'nc_debug_post_put_del_request';
  434.  
  435.         /**
  436.          * No-cache because the current request originated from the server itself.
  437.          *
  438.          * @since 140422 First documented version.
  439.          *
  440.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  441.          */
  442.         const NC_DEBUG_SELF_SERVE_REQUEST = 'nc_debug_self_serve_request';
  443.  
  444.         /**
  445.          * No-cache because the current request is for a feed.
  446.          *
  447.          * @since 140422 First documented version.
  448.          *
  449.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  450.          */
  451.         const NC_DEBUG_FEED_REQUEST = 'nc_debug_feed_request';
  452.  
  453.         /**
  454.          * No-cache because the current request is systematic.
  455.          *
  456.          * @since 140422 First documented version.
  457.          *
  458.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  459.          */
  460.         const NC_DEBUG_WP_SYSTEMATICS = 'nc_debug_wp_systematics';
  461.  
  462.         /**
  463.          * No-cache because the current request is for an administrative area.
  464.          *
  465.          * @since 140422 First documented version.
  466.          *
  467.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  468.          */
  469.         const NC_DEBUG_WP_ADMIN = 'nc_debug_wp_admin';
  470.  
  471.         /**
  472.          * No-cache because the current request is multisite `/files/`.
  473.          *
  474.          * @since 140422 First documented version.
  475.          *
  476.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  477.          */
  478.         const NC_DEBUG_MS_FILES = 'nc_debug_ms_files';
  479.  
  480.         /**
  481.          * No-cache because the current user is like a logged-in user.
  482.          *
  483.          * @since 140422 First documented version.
  484.          *
  485.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  486.          */
  487.         const NC_DEBUG_IS_LIKE_LOGGED_IN_USER = 'nc_debug_is_like_logged_in_user';
  488.  
  489.         /**
  490.          * No-cache because the current user is logged into the site.
  491.          *
  492.          * @since 140422 First documented version.
  493.          *
  494.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  495.          */
  496.         const NC_DEBUG_IS_LOGGED_IN_USER = 'nc_debug_is_logged_in_user';
  497.  
  498.         /**
  499.          * No-cache because the current request contains a query string.
  500.          *
  501.          * @since 140422 First documented version.
  502.          *
  503.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  504.          */
  505.         const NC_DEBUG_GET_REQUEST_QUERIES = 'nc_debug_get_request_queries';
  506.  
  507.         /**
  508.          * No-cache because the current request is a 404 error.
  509.          *
  510.          * @since 140422 First documented version.
  511.          *
  512.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  513.          */
  514.         const NC_DEBUG_404_REQUEST = 'nc_debug_404_request';
  515.  
  516.         /**
  517.          * No-cache because the requested page is currently in maintenance mode.
  518.          *
  519.          * @since 140422 First documented version.
  520.          *
  521.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  522.          */
  523.         const NC_DEBUG_MAINTENANCE_PLUGIN = 'nc_debug_maintenance_plugin';
  524.  
  525.         /**
  526.          * No-cache because the current request is being compressed by an incompatible ZLIB coding type.
  527.          *
  528.          * @since 140422 First documented version.
  529.          *
  530.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  531.          */
  532.         const NC_DEBUG_OB_ZLIB_CODING_TYPE = 'nc_debug_ob_zlib_coding_type';
  533.  
  534.         /**
  535.          * No-cache because the current request resulted in a WP error message.
  536.          *
  537.          * @since 140422 First documented version.
  538.          *
  539.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  540.          */
  541.         const NC_DEBUG_WP_ERROR_PAGE = 'nc_debug_wp_error_page';
  542.  
  543.         /**
  544.          * No-cache because the current request is serving an uncacheable content type.
  545.          *
  546.          * @since 140422 First documented version.
  547.          *
  548.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  549.          */
  550.         const NC_DEBUG_UNCACHEABLE_CONTENT_TYPE = 'nc_debug_uncacheable_content_type';
  551.  
  552.         /**
  553.          * No-cache because the current request sent a non-2xx & non-404 status code.
  554.          *
  555.          * @since 140422 First documented version.
  556.          *
  557.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  558.          */
  559.         const NC_DEBUG_UNCACHEABLE_STATUS = 'nc_debug_uncacheable_status';
  560.  
  561.         /**
  562.          * No-cache because this is a new 404 error that we are symlinking.
  563.          *
  564.          * @since 140422 First documented version.
  565.          *
  566.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  567.          */
  568.         const NC_DEBUG_1ST_TIME_404_SYMLINK = 'nc_debug_1st_time_404_symlink';
  569.  
  570.         /**
  571.          * No-cache because we detected an early buffer termination.
  572.          *
  573.          * @since 140605 Improving output buffer.
  574.          *
  575.          * @var string A unique string identifier in the set of `NC_DEBUG_` constants.
  576.          */
  577.         const NC_DEBUG_EARLY_BUFFER_TERMINATION = 'nc_debug_early_buffer_termination';
  578.  
  579.         /**
  580.          * Class constructor/cache handler.
  581.          *
  582.          * @since 140422 First documented version.
  583.          */
  584.         public function __construct()
  585.         {
  586.             if(!WP_CACHE || !QUICK_CACHE_ENABLE)
  587.                 return; // Not enabled.
  588.  
  589.             if(defined('WP_INSTALLING') || defined('RELOCATE'))
  590.                 return; // N/A; installing|relocating.
  591.  
  592.             $this->is_running  = TRUE;
  593.             $this->timer       = microtime(TRUE);
  594.             $this->text_domain = str_replace('_', '-', __NAMESPACE__);
  595.  
  596.             $this->load_ac_plugins();
  597.             $this->register_shutdown_flag();
  598.             $this->maybe_stop_browser_caching();
  599.             $this->maybe_start_output_buffering();
  600.         }
  601.  
  602.         /**
  603.          * Loads any advanced cache plugin files found inside `/wp-content/ac-plugins`.
  604.          *
  605.          * @since 140422 First documented version.
  606.          */
  607.         public function load_ac_plugins()
  608.         {
  609.             if(!is_dir(WP_CONTENT_DIR.'/ac-plugins'))
  610.                 return; // Nothing to do here.
  611.  
  612.             $GLOBALS[__NAMESPACE__.'__advanced_cache']
  613.                 = $this; // Define now; so it's available for plugins.
  614.  
  615.             foreach((array)glob(WP_CONTENT_DIR.'/ac-plugins/*.php') as $_ac_plugin)
  616.                 if(is_file($_ac_plugin)) include_once $_ac_plugin;
  617.             unset($_ac_plugin); // Houskeeping.
  618.         }
  619.  
  620.         /**
  621.          * Registers a shutdown flag.
  622.          *
  623.          * @since 140605 Improving output buffer.
  624.          *
  625.          * @note In `/wp-settings.php`, Quick Cache is loaded before WP registers its own shutdown function.
  626.          * Therefore, this flag is set before {@link shutdown_action_hook()} fires, and thus before {@link wp_ob_end_flush_all()}.
  627.          *
  628.          * @see http://www.php.net/manual/en/function.register-shutdown-function.php
  629.          */
  630.         public function register_shutdown_flag()
  631.         {
  632.             register_shutdown_function(function ()
  633.             {
  634.                 $GLOBALS[__NAMESPACE__.'__shutdown_flag'] = -1;
  635.             });
  636.         }
  637.  
  638.         /**
  639.          * Sends no-cache headers (if applicable).
  640.          *
  641.          * @since 140422 First documented version.
  642.          */
  643.         public function maybe_stop_browser_caching()
  644.         {
  645.             if(QUICK_CACHE_ALLOW_BROWSER_CACHE)
  646.                 return; // Allow in this case.
  647.  
  648.             if(!empty($_GET['qcABC']) && filter_var($_GET['qcABC'], FILTER_VALIDATE_BOOLEAN))
  649.                 return; // The query var says it's OK here.
  650.  
  651.             header_remove('Last-Modified');
  652.             header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
  653.             header('Cache-Control: no-cache, must-revalidate, max-age=0');
  654.             header('Pragma: no-cache');
  655.         }
  656.  
  657.         /**
  658.          * Start output buffering (if applicable); or serve a cache file (if possible).
  659.          *
  660.          * @since 140422 First documented version.
  661.          *
  662.          * @note This is a vital part of Quick Cache. This method serves existing (fresh) cache files.
  663.          *    It is also responsible for beginning the process of collecting the output buffer.
  664.          */
  665.         public function maybe_start_output_buffering()
  666.         {
  667.             if(strcasecmp(PHP_SAPI, 'cli') === 0)
  668.                 return $this->maybe_set_debug_info($this::NC_DEBUG_PHP_SAPI_CLI);
  669.  
  670.             if(empty($_SERVER['HTTP_HOST']))
  671.                 return $this->maybe_set_debug_info($this::NC_DEBUG_NO_SERVER_HTTP_HOST);
  672.  
  673.             if(empty($_SERVER['REQUEST_URI']))
  674.                 return $this->maybe_set_debug_info($this::NC_DEBUG_NO_SERVER_REQUEST_URI);
  675.  
  676.             if(isset($_GET['qcAC']) && !filter_var($_GET['qcAC'], FILTER_VALIDATE_BOOLEAN))
  677.                 return $this->maybe_set_debug_info($this::NC_DEBUG_QCAC_GET_VAR);
  678.  
  679.             if(defined('QUICK_CACHE_ALLOWED') && !QUICK_CACHE_ALLOWED)
  680.                 return $this->maybe_set_debug_info($this::NC_DEBUG_QUICK_CACHE_ALLOWED_CONSTANT);
  681.  
  682.             if(isset($_SERVER['QUICK_CACHE_ALLOWED']) && !$_SERVER['QUICK_CACHE_ALLOWED'])
  683.                 return $this->maybe_set_debug_info($this::NC_DEBUG_QUICK_CACHE_ALLOWED_SERVER_VAR);
  684.  
  685.             if(defined('DONOTCACHEPAGE'))
  686.                 return $this->maybe_set_debug_info($this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT);
  687.  
  688.             if(isset($_SERVER['DONOTCACHEPAGE']))
  689.                 return $this->maybe_set_debug_info($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR);
  690.  
  691.             if($this->is_post_put_del_request())
  692.                 return $this->maybe_set_debug_info($this::NC_DEBUG_POST_PUT_DEL_REQUEST);
  693.  
  694.             if(isset($_SERVER['REMOTE_ADDR'], $_SERVER['SERVER_ADDR']) && $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR'])
  695.                 if(!$this->is_localhost()) return $this->maybe_set_debug_info($this::NC_DEBUG_SELF_SERVE_REQUEST);
  696.  
  697.             if(!QUICK_CACHE_FEEDS_ENABLE && $this->is_feed())
  698.                 return $this->maybe_set_debug_info($this::NC_DEBUG_FEED_REQUEST);
  699.  
  700.             if(preg_match('/\/(?:wp\-[^\/]+|xmlrpc)\.php(?:[?]|$)/', $_SERVER['REQUEST_URI']))
  701.                 return $this->maybe_set_debug_info($this::NC_DEBUG_WP_SYSTEMATICS);
  702.  
  703.             if(is_admin() || preg_match('/\/wp-admin(?:[\/?]|$)/', $_SERVER['REQUEST_URI']))
  704.                 return $this->maybe_set_debug_info($this::NC_DEBUG_WP_ADMIN);
  705.  
  706.             if(is_multisite() && preg_match('/\/files(?:[\/?]|$)/', $_SERVER['REQUEST_URI']))
  707.                 return $this->maybe_set_debug_info($this::NC_DEBUG_MS_FILES);
  708.  
  709.             if($this->is_like_user_logged_in()) // Commenters, password-protected access, or actually logged-in.
  710.                 return $this->maybe_set_debug_info($this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER);
  711.  
  712.             if(!QUICK_CACHE_GET_REQUESTS && $this->is_get_request_w_query() && (!isset($_GET['qcAC']) || !filter_var($_GET['qcAC'], FILTER_VALIDATE_BOOLEAN)))
  713.                 return $this->maybe_set_debug_info($this::NC_DEBUG_GET_REQUEST_QUERIES);
  714.  
  715.             $this->protocol       = $this->is_ssl() ? 'https://' : 'http://';
  716.             $this->version_salt   = $this->apply_filters(__CLASS__.'__version_salt', '');
  717.             $this->cache_path     = $this->url_to_cache_path($this->protocol.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], '', $this->version_salt);
  718.             $this->cache_file     = QUICK_CACHE_DIR.'/'.$this->cache_path; // NOT considering a user cache at all in the lite version.
  719.             $this->cache_file_404 = QUICK_CACHE_DIR.'/'.$this->url_to_cache_path($this->protocol.$_SERVER['HTTP_HOST'].'/'.QUICK_CACHE_404_CACHE_FILENAME);
  720.             $this->salt_location  = ltrim($this->version_salt.' '.$this->protocol.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
  721.  
  722.             if(is_file($this->cache_file) && filemtime($this->cache_file) >= strtotime('-'.QUICK_CACHE_MAX_AGE))
  723.             {
  724.                 list($headers, $cache) = explode('<!--headers-->', file_get_contents($this->cache_file), 2);
  725.  
  726.                 $headers_list = headers_list(); // Headers already sent (or ready to be sent).
  727.                 foreach(unserialize($headers) as $_header) // Preserves original headers sent with this file.
  728.                     if(!in_array($_header, $headers_list) && stripos($_header, 'Last-Modified:') !== 0) header($_header);
  729.                 unset($_header); // Just a little housekeeping.
  730.  
  731.                 if(QUICK_CACHE_DEBUGGING_ENABLE && $this->is_html_xml_doc($cache)) // Only if HTML comments are possible.
  732.                 {
  733.                     $total_time = number_format(microtime(TRUE) - $this->timer, 5, '.', '');
  734.                     $cache .= "\n".'<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->';
  735.                     // translators: This string is actually NOT translatable because the `__()` function is not available at this point in the processing.
  736.                     $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('Quick Cache fully functional :-) Cache file served for (%1$s) in %2$s seconds, on: %3$s.', $this->text_domain), $this->salt_location, $total_time, date('M jS, Y @ g:i a T'))).' -->';
  737.                 }
  738.                 exit($cache); // Exit with cache contents.
  739.             }
  740.             else // Start buffering output; we may need to cache the HTML generated by this request.
  741.             {
  742.                 ob_start(array($this, 'output_buffer_callback_handler')); // Start output buffering.
  743.             }
  744.             return NULL; // Return value not applicable.
  745.         }
  746.  
  747.         /**
  748.          * Used to setup debug info (if enabled).
  749.          *
  750.          * @since 140422 First documented version.
  751.          *
  752.          * @param string $reason_code One of the `NC_DEBUG_` constants.
  753.          * @param string $reason Optionally override the built-in description with a custom message.
  754.          */
  755.         public function maybe_set_debug_info($reason_code, $reason = '')
  756.         {
  757.             if(!QUICK_CACHE_DEBUGGING_ENABLE)
  758.                 return; // Nothing to do.
  759.  
  760.             $reason = (string)$reason;
  761.             if(!($reason_code = (string)$reason_code))
  762.                 return; // Not applicable.
  763.  
  764.             $this->debug_info = array('reason_code' => $reason_code, 'reason' => $reason);
  765.         }
  766.  
  767.         /**
  768.          * Filters WP {@link \status_header()} (if applicable).
  769.          *
  770.          * @since 140422 First documented version.
  771.          */
  772.         public function maybe_filter_status_header_postload()
  773.         {
  774.             if(empty($this->postload['filter_status_header']))
  775.                 return; // Nothing to do in this case.
  776.  
  777.             $_this = $this; // Reference needed by the closure below.
  778.             add_filter('status_header', function ($status_header, $status_code) use ($_this)
  779.             {
  780.                 if($status_code > 0) // Sending a status?
  781.                     $_this->http_status = (integer)$status_code;
  782.  
  783.                 return $status_header; // Pass through this filter.
  784.  
  785.             }, PHP_INT_MAX, 2);
  786.         }
  787.  
  788.         /**
  789.          * Hooks `NC_DEBUG_` info into the WordPress `shutdown` phase (if applicable).
  790.          *
  791.          * @since 140422 First documented version.
  792.          */
  793.         public function maybe_set_debug_info_postload()
  794.         {
  795.             if(!QUICK_CACHE_DEBUGGING_ENABLE)
  796.                 return; // Nothing to do.
  797.  
  798.             if(empty($this->postload['set_debug_info']))
  799.                 return; // Nothing to do in this case.
  800.  
  801.             if(is_admin()) return; // Not applicable.
  802.  
  803.             if(strcasecmp(PHP_SAPI, 'cli') === 0)
  804.                 return; // Let's not run the risk here.
  805.  
  806.             add_action('shutdown', array($this, 'maybe_echo_nc_debug_info'), PHP_INT_MAX - 10);
  807.         }
  808.  
  809.         /**
  810.          * Grab details from WP and the Quick Cache plugin itself,
  811.          *    after the main query is loaded (if at all possible).
  812.          *
  813.          * This is where we have a chance to grab any values we need from WordPress; or from the QC plugin.
  814.          *    It is EXTREMEMLY important that we NOT attempt to grab any object references here.
  815.          *    Anything acquired in this phase should be stored as a scalar value.
  816.          *    See {@link output_buffer_callback_handler()} for further details.
  817.          *
  818.          * @since 140422 First documented version.
  819.          *
  820.          * @attaches-to `wp` hook.
  821.          */
  822.         public function wp_main_query_postload()
  823.         {
  824.             if(empty($this->postload['wp_main_query']))
  825.                 return; // Nothing to do in this case.
  826.  
  827.             if($this->is_wp_loaded_query || is_admin())
  828.                 return; // Nothing to do.
  829.  
  830.             if(!is_main_query())
  831.                 return; // Not the main query.
  832.  
  833.             $this->is_wp_loaded_query   = TRUE;
  834.             $this->is_404               = is_404();
  835.             $this->site_url             = site_url();
  836.             $this->home_url             = home_url();
  837.             $this->is_user_logged_in    = is_user_logged_in();
  838.             $this->is_maintenance       = function_exists('is_maintenance') && is_maintenance();
  839.             $this->is_a_wp_content_type = $this->is_404 || $this->is_maintenance || is_front_page() || is_home() || is_singular() || is_archive() || is_post_type_archive() || is_tax() || is_search() || is_feed();
  840.  
  841.             if(function_exists('\\'.__NAMESPACE__.'\\plugin'))
  842.                 $this->plugin_file = plugin()->file;
  843.         }
  844.  
  845.         /**
  846.          * Output buffer handler; i.e. the cache file generator.
  847.          *
  848.          * @note We CANNOT depend on any WP functionality here; it will cause problems.
  849.          *    Anything we need from WP should be saved in the postload phase as a scalar value.
  850.          *
  851.          * @since 140422 First documented version.
  852.          *
  853.          * @param string  $buffer The buffer from {@link \ob_start()}.
  854.          * @param integer $phase A set of bitmask flags.
  855.          *
  856.          * @return string|boolean The output buffer, or `FALSE` to indicate no change.
  857.          *
  858.          * @throws \exception If unable to handle output buffering for any reason.
  859.          *
  860.          * @attaches-to {@link \ob_start()}
  861.          */
  862.         public function output_buffer_callback_handler($buffer, $phase)
  863.         {
  864.             if(!($phase & PHP_OUTPUT_HANDLER_END)) // We do NOT chunk the buffer; so this should NOT occur.
  865.                 throw new \exception(sprintf(__('Unexpected OB phase: `%1$s`.', $this->text_domain), $phase));
  866.  
  867.             # Exclusion checks; there are MANY of these...
  868.  
  869.             $cache = trim((string)$buffer);
  870.             if(!isset($cache[0])) // Allows a `0`.
  871.                 return FALSE; // Don't cache an empty buffer.
  872.  
  873.             if(!isset($GLOBALS[__NAMESPACE__.'__shutdown_flag']))
  874.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_EARLY_BUFFER_TERMINATION);
  875.  
  876.             if(isset($_GET['qcAC']) && !filter_var($_GET['qcAC'], FILTER_VALIDATE_BOOLEAN))
  877.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_QCAC_GET_VAR);
  878.  
  879.             if(defined('QUICK_CACHE_ALLOWED') && !QUICK_CACHE_ALLOWED)
  880.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_QUICK_CACHE_ALLOWED_CONSTANT);
  881.  
  882.             if(isset($_SERVER['QUICK_CACHE_ALLOWED']) && !$_SERVER['QUICK_CACHE_ALLOWED'])
  883.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_QUICK_CACHE_ALLOWED_SERVER_VAR);
  884.  
  885.             if(defined('DONOTCACHEPAGE')) // WP Super Cache compatible.
  886.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT);
  887.  
  888.             if(isset($_SERVER['DONOTCACHEPAGE'])) // WP Super Cache compatible.
  889.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR);
  890.  
  891.             if($this->is_user_logged_in) // Actually logged into the site.
  892.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_IS_LOGGED_IN_USER);
  893.  
  894.             if($this->is_like_user_logged_in()) // Commenters, password-protected access, or actually logged-in.
  895.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER); // This uses a separate debug notice.
  896.  
  897.             if($this->is_404 && !QUICK_CACHE_CACHE_404_REQUESTS) // Not caching 404 errors.
  898.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_404_REQUEST);
  899.  
  900.             if(strpos($cache, '<body id="error-page">') !== FALSE)
  901.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_WP_ERROR_PAGE);
  902.  
  903.             if(!function_exists('http_response_code') && stripos($cache, '<title>database error</title>') !== FALSE)
  904.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_WP_ERROR_PAGE);
  905.  
  906.             if(!$this->has_a_cacheable_content_type()) // Exclude non-HTML/XML content types.
  907.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_UNCACHEABLE_CONTENT_TYPE);
  908.  
  909.             if(!$this->has_a_cacheable_status()) // This will catch WP Maintenance Mode too.
  910.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_UNCACHEABLE_STATUS);
  911.  
  912.             if($this->is_maintenance) // <http://wordpress.org/extend/plugins/maintenance-mode>
  913.                 return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_MAINTENANCE_PLUGIN);
  914.  
  915.             if(function_exists('zlib_get_coding_type') && zlib_get_coding_type()
  916.                && (!($zlib_oc = ini_get('zlib.output_compression')) || !filter_var($zlib_oc, FILTER_VALIDATE_BOOLEAN))
  917.             ) return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_OB_ZLIB_CODING_TYPE);
  918.  
  919.             # Cache directory checks. The cache file directory is created here if necessary.
  920.  
  921.             if(!is_dir(QUICK_CACHE_DIR) && mkdir(QUICK_CACHE_DIR, 0775, TRUE) && !is_file(QUICK_CACHE_DIR.'/.htaccess'))
  922.                 file_put_contents(QUICK_CACHE_DIR.'/.htaccess', $this->htaccess_deny); // We know it's writable here.
  923.  
  924.             if(!is_dir($cache_file_dir = dirname($this->cache_file))) $cache_file_dir_writable = mkdir($cache_file_dir, 0775, TRUE);
  925.             if(empty($cache_file_dir_writable) && !is_writable($cache_file_dir)) // Only check if it's writable, if we didn't just successfully create it.
  926.                 throw new \exception(sprintf(__('Cache directory not writable. Quick Cache needs this directory please: `%1$s`. Set permissions to `755` or higher; `777` might be needed in some cases.', $this->text_domain), $cache_file_dir));
  927.  
  928.             # This is where a new 404 request might be detected for the first time; and where the 404 error file already exists in this case.
  929.  
  930.             if($this->is_404 && is_file($this->cache_file_404))
  931.                 if(!symlink($this->cache_file_404, $this->cache_file))
  932.                     throw new \exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', $this->text_domain), $this->cache_file, $this->cache_file_404, QUICK_CACHE_DIR));
  933.                 else return (boolean)$this->maybe_set_debug_info($this::NC_DEBUG_1ST_TIME_404_SYMLINK);
  934.  
  935.             /* ------- Otherwise, we need to construct & store a new cache file. -------- */
  936.  
  937.             if(QUICK_CACHE_DEBUGGING_ENABLE && $this->is_html_xml_doc($cache)) // Only if HTML comments are possible.
  938.             {
  939.                 $total_time = number_format(microtime(TRUE) - $this->timer, 5, '.', '');
  940.                 $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('Quick Cache file path: %1$s', $this->text_domain), str_replace(ABSPATH, '', $this->is_404 ? $this->cache_file_404 : $this->cache_file))).' -->';
  941.                 $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('Quick Cache file built for (%1$s) in %2$s seconds, on: %3$s.', $this->text_domain),
  942.                                                                 ($this->is_404) ? '404 [error document]' : $this->salt_location, $total_time, date('M jS, Y @ g:i a T'))).' -->';
  943.                 $cache .= "\n".'<!-- '.htmlspecialchars(sprintf(__('This Quick Cache file will auto-expire (and be rebuilt) on: %1$s (based on your configured expiration time).', $this->text_domain), date('M jS, Y @ g:i a T', strtotime('+'.QUICK_CACHE_MAX_AGE)))).' -->';
  944.             }
  945.             $cache_file_tmp = $this->cache_file.'.'.uniqid('', TRUE).'.tmp'; // Cache creation is atomic; e.g. tmp file w/ rename.
  946.             /*
  947.              * This is NOT a 404, or it is 404 and the 404 cache file doesn't yet exist (so we need to create it).
  948.              */
  949.             if($this->is_404) // This is a 404; let's create 404 cache file and symlink to it.
  950.             {
  951.                 if(file_put_contents($cache_file_tmp, serialize(headers_list()).'<!--headers-->'.$cache) && rename($cache_file_tmp, $this->cache_file_404))
  952.                     if(symlink($this->cache_file_404, $this->cache_file)) // If this fails an exception will be thrown down below.
  953.                         return $cache; // Return the newly built cache; with possible debug information also.
  954.  
  955.             } // NOT a 404; let's write a new cache file.
  956.             else if(file_put_contents($cache_file_tmp, serialize(headers_list()).'<!--headers-->'.$cache) && rename($cache_file_tmp, $this->cache_file))
  957.                 return $cache; // Return the newly built cache; with possible debug information also.
  958.  
  959.             @unlink($cache_file_tmp); // Clean this up (if it exists); and throw an exception with information for the site owner.
  960.             throw new \exception(sprintf(__('Quick Cache: failed to write cache file for: `%1$s`; possible permissions issue (or race condition), please check your cache directory: `%2$s`.', $this->text_domain), $_SERVER['REQUEST_URI'], QUICK_CACHE_DIR));
  961.         }
  962.  
  963.         /**
  964.          * Echoes `NC_DEBUG_` info in the WordPress `shutdown` phase (if applicable).
  965.          *
  966.          * @since 140605 Improving debug info output phase.
  967.          *
  968.          * @attaches-to `shutdown` hook in WordPress w/ a late priority.
  969.          */
  970.         public function maybe_echo_nc_debug_info() // Debug info in the shutdown phase.
  971.         {
  972.             if(!QUICK_CACHE_DEBUGGING_ENABLE)
  973.                 return; // Nothing to do.
  974.  
  975.             if(is_admin()) return; // Not applicable.
  976.  
  977.             if(strcasecmp(PHP_SAPI, 'cli') === 0)
  978.                 return; // Let's not run the risk here.
  979.  
  980.             if($this->debug_info && $this->has_a_cacheable_content_type() && $this->is_a_wp_content_type)
  981.                 echo (string)$this->maybe_get_nc_debug_info($this->debug_info['reason_code'], $this->debug_info['reason']);
  982.         }
  983.  
  984.         /**
  985.          * Gets `NC_DEBUG_` info (if applicable).
  986.          *
  987.          * @since 140422 First documented version.
  988.          *
  989.          * @param string $reason_code One of the `NC_DEBUG_` constants.
  990.          * @param string $reason Optional; to override the default description with a custom message.
  991.          *
  992.          * @return string The debug info; i.e. full description (if applicable).
  993.          */
  994.         public function maybe_get_nc_debug_info($reason_code = '', $reason = '')
  995.         {
  996.             if(!QUICK_CACHE_DEBUGGING_ENABLE)
  997.                 return ''; // Not applicable.
  998.  
  999.             $reason = (string)$reason;
  1000.             if(!($reason_code = (string)$reason_code))
  1001.                 return ''; // Not applicable.
  1002.  
  1003.             if(!$reason) switch($reason_code)
  1004.             {
  1005.                 case $this::NC_DEBUG_PHP_SAPI_CLI:
  1006.                     $reason = __('because `PHP_SAPI` reports that you are currently running from the command line.', $this->text_domain);
  1007.                     break; // Break switch handler.
  1008.  
  1009.                 case $this::NC_DEBUG_QCAC_GET_VAR:
  1010.                     $reason = __('because `$_GET[\'qcAC\']` is set to a boolean-ish FALSE value.', $this->text_domain);
  1011.                     break; // Break switch handler.
  1012.  
  1013.                 case $this::NC_DEBUG_NO_SERVER_HTTP_HOST:
  1014.                     $reason = __('because `$_SERVER[\'HTTP_HOST\']` is missing from your server configuration.', $this->text_domain);
  1015.                     break; // Break switch handler.
  1016.  
  1017.                 case $this::NC_DEBUG_NO_SERVER_REQUEST_URI:
  1018.                     $reason = __('because `$_SERVER[\'REQUEST_URI\']` is missing from your server configuration.', $this->text_domain);
  1019.                     break; // Break switch handler.
  1020.  
  1021.                 case $this::NC_DEBUG_QUICK_CACHE_ALLOWED_CONSTANT:
  1022.                     $reason = __('because the PHP constant `QUICK_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', $this->text_domain);
  1023.                     break; // Break switch handler.
  1024.  
  1025.                 case $this::NC_DEBUG_QUICK_CACHE_ALLOWED_SERVER_VAR:
  1026.                     $reason = __('because the environment variable `$_SERVER[\'QUICK_CACHE_ALLOWED\']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', $this->text_domain);
  1027.                     break; // Break switch handler.
  1028.  
  1029.                 case $this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT:
  1030.                     $reason = __('because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', $this->text_domain);
  1031.                     break; // Break switch handler.
  1032.  
  1033.                 case $this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR:
  1034.                     $reason = __('because the environment variable `$_SERVER[\'DONOTCACHEPAGE\']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', $this->text_domain);
  1035.                     break; // Break switch handler.
  1036.  
  1037.                 case $this::NC_DEBUG_POST_PUT_DEL_REQUEST:
  1038.                     $reason = __('because `$_SERVER[\'REQUEST_METHOD\']` is `POST`, `PUT` or `DELETE`. These request types should never (ever) be cached in any way.', $this->text_domain);
  1039.                     break; // Break switch handler.
  1040.  
  1041.                 case $this::NC_DEBUG_SELF_SERVE_REQUEST:
  1042.                     $reason = __('because `$_SERVER[\'REMOTE_ADDR\']` === `$_SERVER[\'SERVER_ADDR\']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define(\'LOCALHOST\', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web.', $this->text_domain);
  1043.                     break; // Break switch handler.
  1044.  
  1045.                 case $this::NC_DEBUG_FEED_REQUEST:
  1046.                     $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds.', $this->text_domain);
  1047.                     break; // Break switch handler.
  1048.  
  1049.                 case $this::NC_DEBUG_WP_SYSTEMATICS:
  1050.                     $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way.', $this->text_domain);
  1051.                     break; // Break switch handler.
  1052.  
  1053.                 case $this::NC_DEBUG_WP_ADMIN:
  1054.                     $reason = __('because `$_SERVER[\'REQUEST_URI\']` or the `is_admin()` function indicates this is an administrative area of the site.', $this->text_domain);
  1055.                     break; // Break switch handler.
  1056.  
  1057.                 case $this::NC_DEBUG_MS_FILES:
  1058.                     $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page.', $this->text_domain);
  1059.                     break; // Break switch handler.
  1060.  
  1061.                 case $this::NC_DEBUG_IS_LOGGED_IN_USER:
  1062.                 case $this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER:
  1063.                     $reason = __('because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you\'ve left (or replied to) a comment recently. If this message continues, please clear your cookies and try again.', $this->text_domain);
  1064.                     break; // Break switch handler.
  1065.  
  1066.                 case $this::NC_DEBUG_GET_REQUEST_QUERIES:
  1067.                     $reason = __('because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string.', $this->text_domain);
  1068.                     break; // Break switch handler.
  1069.  
  1070.                 case $this::NC_DEBUG_404_REQUEST:
  1071.                     $reason = __('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors.', $this->text_domain);
  1072.                     break; // Break switch handler.
  1073.  
  1074.                 case $this::NC_DEBUG_MAINTENANCE_PLUGIN:
  1075.                     $reason = __('because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time.', $this->text_domain);
  1076.                     break; // Break switch handler.
  1077.  
  1078.                 case $this::NC_DEBUG_OB_ZLIB_CODING_TYPE:
  1079.                     $reason = __('because Quick Cache is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. Quick Cache is NOT compatible with `ob_gzhandler()` and others like this.', $this->text_domain);
  1080.                     break; // Break switch handler.
  1081.  
  1082.                 case $this::NC_DEBUG_WP_ERROR_PAGE:
  1083.                     $reason = __('because the contents of this document contain `<body id="error-page">`, which indicates this is an auto-generated WordPress error message.', $this->text_domain);
  1084.                     break; // Break switch handler.
  1085.  
  1086.                 case $this::NC_DEBUG_UNCACHEABLE_CONTENT_TYPE:
  1087.                     $reason = __('because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', $this->text_domain);
  1088.                     break; // Break switch handler.
  1089.  
  1090.                 case $this::NC_DEBUG_UNCACHEABLE_STATUS:
  1091.                     $reason = __('because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', $this->text_domain);
  1092.                     break; // Break switch handler.
  1093.  
  1094.                 case $this::NC_DEBUG_1ST_TIME_404_SYMLINK:
  1095.                     $reason = __('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it\'s happened on this page. Your current configuration says that 404 errors SHOULD be cached, so Quick Cache built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don\'t clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request.', $this->text_domain);
  1096.                     break; // Break switch handler.
  1097.  
  1098.                 case $this::NC_DEBUG_EARLY_BUFFER_TERMINATION:
  1099.                     $reason = __('because Quick Cache detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It\'s not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since Quick Cache is effectively disabled at runtime when this occurs.', $this->text_domain);
  1100.                     break; // Break switch handler.
  1101.  
  1102.                 default: // Default case handler.
  1103.                     $reason = __('due to an unexpected behavior in the application. Please report this as a bug!', $this->text_domain);
  1104.                     break; // Break switch handler.
  1105.             }
  1106.             return "\n".'<!-- '.htmlspecialchars(sprintf(__('Quick Cache is NOT caching this page, %1$s', $this->text_domain), $reason)).' -->';
  1107.         }
  1108.  
  1109.         /*
  1110.          * See also: `quick-cache.inc.php` duplicates.
  1111.          *    @TODO Find a way to centralize this section so it can be shared between both classes easily.
  1112.          */
  1113.  
  1114.         /**
  1115.          * Exclude scheme from cache path.
  1116.          *
  1117.          * @since 140422 First documented version.
  1118.          *
  1119.          * @var integer Part of a bitmask.
  1120.          */
  1121.         const CACHE_PATH_NO_SCHEME = 1;
  1122.  
  1123.         /**
  1124.          * Exclude host (i.e. domain name) from cache path.
  1125.          *
  1126.          * @since 140422 First documented version.
  1127.          *
  1128.          * @var integer Part of a bitmask.
  1129.          */
  1130.         const CACHE_PATH_NO_HOST = 2;
  1131.  
  1132.         /**
  1133.          * Exclude path from cache path.
  1134.          *
  1135.          * @since 140422 First documented version.
  1136.          *
  1137.          * @var integer Part of a bitmask.
  1138.          */
  1139.         const CACHE_PATH_NO_PATH = 4;
  1140.  
  1141.         /**
  1142.          * Exclude path index (i.e. no default `index`) from cache path.
  1143.          *
  1144.          * @since 140422 First documented version.
  1145.          *
  1146.          * @var integer Part of a bitmask.
  1147.          */
  1148.         const CACHE_PATH_NO_PATH_INDEX = 8;
  1149.  
  1150.         /**
  1151.          * Exclude query, user & version salt from cache path.
  1152.          *
  1153.          * @since 140422 First documented version.
  1154.          *
  1155.          * @var integer Part of a bitmask.
  1156.          */
  1157.         const CACHE_PATH_NO_QUV = 16;
  1158.  
  1159.         /**
  1160.          * Exclude query string from cache path.
  1161.          *
  1162.          * @since 140422 First documented version.
  1163.          *
  1164.          * @var integer Part of a bitmask.
  1165.          */
  1166.         const CACHE_PATH_NO_QUERY = 32;
  1167.  
  1168.         /**
  1169.          * Exclude user token from cache path.
  1170.          *
  1171.          * @since 140422 First documented version.
  1172.          *
  1173.          * @var integer Part of a bitmask.
  1174.          */
  1175.         const CACHE_PATH_NO_USER = 64;
  1176.  
  1177.         /**
  1178.          * Exclude version salt from cache path.
  1179.          *
  1180.          * @since 140422 First documented version.
  1181.          *
  1182.          * @var integer Part of a bitmask.
  1183.          */
  1184.         const CACHE_PATH_NO_VSALT = 128;
  1185.  
  1186.         /**
  1187.          * Exclude extension from cache path.
  1188.          *
  1189.          * @since 140422 First documented version.
  1190.          *
  1191.          * @var integer Part of a bitmask.
  1192.          */
  1193.         const CACHE_PATH_NO_EXT = 256;
  1194.  
  1195.         /**
  1196.          * Converts a URL into a `cache/path`.
  1197.          *
  1198.          * @since 140422 First documented version.
  1199.          *
  1200.          * @param string  $url The input URL to convert.
  1201.          * @param string  $with_user_token Optional user token (if applicable).
  1202.          * @param string  $with_version_salt Optional version salt (if applicable).
  1203.          * @param integer $flags Optional flags; a bitmask provided by `CACHE_PATH_*` constants.
  1204.          *
  1205.          * @return string The resulting `cache/path` based on the input `$url`.
  1206.          */
  1207.         public function url_to_cache_path($url, $with_user_token = '', $with_version_salt = '', $flags = 0)
  1208.         {
  1209.             $cache_path        = ''; // Initialize.
  1210.             $url               = trim((string)$url);
  1211.             $with_user_token   = trim((string)$with_user_token);
  1212.             $with_version_salt = trim((string)$with_version_salt);
  1213.  
  1214.             if($url && strpos($url, '://') === FALSE)
  1215.                 $url = '//'.ltrim($url, '/');
  1216.  
  1217.             if(!$url || !($url = parse_url($url)))
  1218.                 return ''; // Invalid URL.
  1219.  
  1220.             if(!($flags & $this::CACHE_PATH_NO_SCHEME))
  1221.             {
  1222.                 if(!empty($url['scheme']))
  1223.                     $cache_path .= $url['scheme'].'/';
  1224.                 else $cache_path .= $this->is_ssl() ? 'https/' : 'http/';
  1225.             }
  1226.             if(!($flags & $this::CACHE_PATH_NO_HOST))
  1227.             {
  1228.                 if(!empty($url['host']))
  1229.                     $cache_path .= $url['host'].'/';
  1230.                 else $cache_path .= $_SERVER['HTTP_HOST'].'/';
  1231.             }
  1232.             if(!($flags & $this::CACHE_PATH_NO_PATH))
  1233.             {
  1234.                 if(!empty($url['path']) && strlen($url['path'] = trim($url['path'], '\\/'." \t\n\r\0\x0B")))
  1235.                     $cache_path .= $url['path'].'/';
  1236.                 else if(!($flags & $this::CACHE_PATH_NO_PATH_INDEX)) $cache_path .= 'index/';
  1237.             }
  1238.             if($this->is_extension_loaded('mbstring') && mb_check_encoding($cache_path, 'UTF-8'))
  1239.                 $cache_path = mb_strtolower($cache_path, 'UTF-8');
  1240.             $cache_path = str_replace('.', '-', strtolower($cache_path));
  1241.  
  1242.             if(!($flags & $this::CACHE_PATH_NO_QUV))
  1243.             {
  1244.                 if(!($flags & $this::CACHE_PATH_NO_QUERY))
  1245.                     if(isset($url['query']) && $url['query'] !== '')
  1246.                         $cache_path = rtrim($cache_path, '/').'.q/'.md5($url['query']).'/';
  1247.  
  1248.                 if(!($flags & $this::CACHE_PATH_NO_USER))
  1249.                     if($with_user_token !== '') // Allow a `0` value if desirable.
  1250.                         $cache_path = rtrim($cache_path, '/').'.u/'.str_replace(array('/', '\\'), '-', $with_user_token).'/';
  1251.  
  1252.                 if(!($flags & $this::CACHE_PATH_NO_VSALT))
  1253.                     if($with_version_salt !== '') // Allow a `0` value if desirable.
  1254.                         $cache_path = rtrim($cache_path, '/').'.v/'.str_replace(array('/', '\\'), '-', $with_version_salt).'/';
  1255.             }
  1256.             $cache_path = trim(preg_replace('/\/+/', '/', $cache_path), '/');
  1257.             $cache_path = preg_replace('/[^a-z0-9\/.]/i', '-', $cache_path);
  1258.  
  1259.             if(!($flags & $this::CACHE_PATH_NO_EXT))
  1260.                 $cache_path .= '.html';
  1261.  
  1262.             return $cache_path;
  1263.         }
  1264.  
  1265.         /**
  1266.          * Produces a token based on the current `$_SERVER['HTTP_HOST']`.
  1267.          *
  1268.          * @since 140422 First documented version.
  1269.          *
  1270.          * @param boolean $dashify Optional, defaults to a `FALSE` value.
  1271.          *    If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`.
  1272.          *
  1273.          * @return string Token based on the current `$_SERVER['HTTP_HOST']`.
  1274.          *
  1275.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1276.          */
  1277.         public function host_token($dashify = FALSE)
  1278.         {
  1279.             $dashify = (integer)$dashify;
  1280.             static $tokens = array(); // Static cache.
  1281.             if(isset($tokens[$dashify])) return $tokens[$dashify];
  1282.  
  1283.             $host        = strtolower($_SERVER['HTTP_HOST']);
  1284.             $token_value = ($dashify) ? trim(preg_replace('/[^a-z0-9\/]/i', '-', $host), '-') : $host;
  1285.  
  1286.             return ($tokens[$dashify] = $token_value);
  1287.         }
  1288.  
  1289.         /**
  1290.          * Produces a token based on the current site's base directory.
  1291.          *
  1292.          * @since 140605 First documented version.
  1293.          *
  1294.          * @param boolean $dashify Optional, defaults to a `FALSE` value.
  1295.          *    If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`.
  1296.          *
  1297.          * @return string Produces a token based on the current site's base directory;
  1298.          *    (i.e. in the case of a sub-directory multisite network).
  1299.          *
  1300.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1301.          *
  1302.          * @see plugin\clear_cache()
  1303.          * @see plugin\update_blog_paths()
  1304.          */
  1305.         public function host_base_token($dashify = FALSE)
  1306.         {
  1307.             $dashify = (integer)$dashify;
  1308.             static $tokens = array(); // Static cache.
  1309.             if(isset($tokens[$dashify])) return $tokens[$dashify];
  1310.  
  1311.             $host_base_token = '/'; // Assume NOT multisite; or running it's own domain.
  1312.  
  1313.             if(is_multisite() && (!defined('SUBDOMAIN_INSTALL') || !SUBDOMAIN_INSTALL))
  1314.             { // Multisite w/ sub-directories; need a valid sub-directory token.
  1315.  
  1316.                 if(defined('PATH_CURRENT_SITE')) $host_base_token = PATH_CURRENT_SITE;
  1317.                 else if(!empty($GLOBALS['base'])) $host_base_token = $GLOBALS['base'];
  1318.  
  1319.                 $host_base_token = trim($host_base_token, '\\/'." \t\n\r\0\x0B");
  1320.                 $host_base_token = (isset($host_base_token[0])) ? '/'.$host_base_token.'/' : '/';
  1321.             }
  1322.             $token_value = ($dashify) ? trim(preg_replace('/[^a-z0-9\/]/i', '-', $host_base_token), '-') : $host_base_token;
  1323.  
  1324.             return ($tokens[$dashify] = $token_value);
  1325.         }
  1326.  
  1327.         /**
  1328.          * Produces a token based on the current blog's sub-directory.
  1329.          *
  1330.          * @since 140422 First documented version.
  1331.          *
  1332.          * @param boolean $dashify Optional, defaults to a `FALSE` value.
  1333.          *    If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`.
  1334.          *
  1335.          * @return string Produces a token based on the current blog sub-directory
  1336.          *    (i.e. in the case of a sub-directory multisite network).
  1337.          *
  1338.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1339.          *
  1340.          * @see plugin\clear_cache()
  1341.          * @see plugin\update_blog_paths()
  1342.          */
  1343.         public function host_dir_token($dashify = FALSE)
  1344.         {
  1345.             $dashify = (integer)$dashify;
  1346.             static $tokens = array(); // Static cache.
  1347.             if(isset($tokens[$dashify])) return $tokens[$dashify];
  1348.  
  1349.             $host_dir_token = '/'; // Assume NOT multisite; or running it's own domain.
  1350.  
  1351.             if(is_multisite() && (!defined('SUBDOMAIN_INSTALL') || !SUBDOMAIN_INSTALL))
  1352.             { // Multisite w/ sub-directories; need a valid sub-directory token.
  1353.  
  1354.                 $uri_minus_base = // Supports `/sub-dir/child-blog-sub-dir/` also.
  1355.                     preg_replace('/^'.preg_quote($this->host_base_token(), '/').'/', '', $_SERVER['REQUEST_URI']);
  1356.  
  1357.                 list($host_dir_token) = explode('/', trim($uri_minus_base, '/'));
  1358.                 $host_dir_token = (isset($host_dir_token[0])) ? '/'.$host_dir_token.'/' : '/';
  1359.  
  1360.                 if($host_dir_token !== '/' // Perhaps NOT the main site?
  1361.                    && (!is_file(QUICK_CACHE_DIR.'/qc-blog-paths') // NOT a read/valid blog path?
  1362.                        || !in_array($host_dir_token, unserialize(file_get_contents(QUICK_CACHE_DIR.'/qc-blog-paths')), TRUE))
  1363.                 ) $host_dir_token = '/'; // Main site; e.g. this is NOT a real/valid child blog path.
  1364.             }
  1365.             $token_value = ($dashify) ? trim(preg_replace('/[^a-z0-9\/]/i', '-', $host_dir_token), '-') : $host_dir_token;
  1366.  
  1367.             return ($tokens[$dashify] = $token_value);
  1368.         }
  1369.  
  1370.         /**
  1371.          * Produces tokens for the current site's base directory & current blog's sub-directory.
  1372.          *
  1373.          * @since 140422 First documented version.
  1374.          *
  1375.          * @param boolean $dashify Optional, defaults to a `FALSE` value.
  1376.          *    If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`.
  1377.          *
  1378.          * @return string Tokens for the current site's base directory & current blog's sub-directory.
  1379.          *
  1380.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1381.          *
  1382.          * @see clear_cache()
  1383.          * @see update_blog_paths()
  1384.          */
  1385.         public function host_base_dir_tokens($dashify = FALSE)
  1386.         {
  1387.             return preg_replace('/\/{2,}/', '/', $this->host_base_token($dashify).$this->host_dir_token($dashify));
  1388.         }
  1389.  
  1390.         /**
  1391.          * Is the current request method `POST|PUT|DELETE`?
  1392.          *
  1393.          * @since 140422 First documented version.
  1394.          *
  1395.          * @return boolean `TRUE` if yes; else `FALSE`.
  1396.          *
  1397.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1398.          */
  1399.         public function is_post_put_del_request()
  1400.         {
  1401.             static $is; // Cache.
  1402.             if(isset($is)) return $is;
  1403.  
  1404.             if(!empty($_SERVER['REQUEST_METHOD']))
  1405.                 if(in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT', 'DELETE'), TRUE))
  1406.                     return ($is = TRUE);
  1407.  
  1408.             return ($is = FALSE);
  1409.         }
  1410.  
  1411.         /**
  1412.          * Does the current request include a query string?
  1413.          *
  1414.          * @since 140422 First documented version.
  1415.          *
  1416.          * @return boolean `TRUE` if yes; else `FALSE`.
  1417.          *
  1418.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1419.          */
  1420.         public function is_get_request_w_query()
  1421.         {
  1422.             static $is; // Cache.
  1423.             if(isset($is)) return $is;
  1424.  
  1425.             if(!empty($_GET) || isset($_SERVER['QUERY_STRING'][0]))
  1426.                 if(!(isset($_GET['qcABC']) && count($_GET) === 1)) // Ignore this special case.
  1427.                     return ($is = TRUE);
  1428.  
  1429.             return ($is = FALSE);
  1430.         }
  1431.  
  1432.         /**
  1433.          * Should the current user be considered a logged-in user?
  1434.          *
  1435.          * @since 140422 First documented version.
  1436.          *
  1437.          * @return boolean `TRUE` if yes; else `FALSE`.
  1438.          *
  1439.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1440.          */
  1441.         public function is_like_user_logged_in()
  1442.         {
  1443.             static $is; // Cache.
  1444.             if(isset($is)) return $is;
  1445.  
  1446.             /* This checks for a PHP session; i.e. session_start() in PHP where you're dealing with a user session.
  1447.              * WordPress itself does not use sessions, but some plugins/themes do. If you have a theme/plugin using
  1448.              * sessions, and there is an active session open, we consider you logged in; and thus, no caching.
  1449.              * SID is a PHP internal constant to identify a PHP session. It's the same regardless of the app. If PHP
  1450.              * starts a session, SID is defined.
  1451.              */
  1452.             if(defined('SID') && SID) return ($is = TRUE); // Session.
  1453.  
  1454.             $logged_in_cookies[] = 'comment_author_'; // Comment (and/or reply) authors.
  1455.             $logged_in_cookies[] = 'wp-postpass_'; // Password access to protected posts.
  1456.  
  1457.             $logged_in_cookies[] = (defined('AUTH_COOKIE')) ? AUTH_COOKIE : 'wordpress_';
  1458.             $logged_in_cookies[] = (defined('SECURE_AUTH_COOKIE')) ? SECURE_AUTH_COOKIE : 'wordpress_sec_';
  1459.             $logged_in_cookies[] = (defined('LOGGED_IN_COOKIE')) ? LOGGED_IN_COOKIE : 'wordpress_logged_in_';
  1460.             $logged_in_cookies   = '/^(?:'.implode('|', array_map(function ($logged_in_cookie)
  1461.                 {
  1462.                     return preg_quote($logged_in_cookie, '/'); // Escape.
  1463.  
  1464.                 }, $logged_in_cookies)).')/';
  1465.             $test_cookie         = (defined('TEST_COOKIE')) ? TEST_COOKIE : 'wordpress_test_cookie';
  1466.  
  1467.             foreach($_COOKIE as $_key => $_value) if($_key !== $test_cookie)
  1468.                 if(preg_match($logged_in_cookies, $_key) && $_value) return ($is = TRUE);
  1469.             unset($_key, $_value); // Housekeeping.
  1470.  
  1471.             return ($is = FALSE);
  1472.         }
  1473.  
  1474.         /**
  1475.          * Are we in a LOCALHOST environment?
  1476.          *
  1477.          * @since 140422 First documented version.
  1478.          *
  1479.          * @return boolean `TRUE` if yes; else `FALSE`.
  1480.          *
  1481.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1482.          */
  1483.         public function is_localhost()
  1484.         {
  1485.             static $is; // Cache.
  1486.             if(isset($is)) return $is;
  1487.  
  1488.             if(defined('LOCALHOST') && LOCALHOST) return ($is = TRUE);
  1489.  
  1490.             if(!defined('LOCALHOST') && !empty($_SERVER['HTTP_HOST']))
  1491.                 if(preg_match('/localhost|127\.0\.0\.1/i', $_SERVER['HTTP_HOST']))
  1492.                     return ($is = TRUE);
  1493.  
  1494.             return ($is = FALSE);
  1495.         }
  1496.  
  1497.         /**
  1498.          * Is the current request for a feed?
  1499.          *
  1500.          * @since 140422 First documented version.
  1501.          *
  1502.          * @return boolean `TRUE` if yes; else `FALSE`.
  1503.          *
  1504.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1505.          */
  1506.         public function is_feed()
  1507.         {
  1508.             static $is; // Cache.
  1509.             if(isset($is)) return $is;
  1510.  
  1511.             if(preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI']))
  1512.                 return ($is = TRUE);
  1513.  
  1514.             if(isset($_REQUEST['feed']))
  1515.                 return ($is = TRUE);
  1516.  
  1517.             return ($is = FALSE);
  1518.         }
  1519.  
  1520.         /**
  1521.          * Is the current request over SSL?
  1522.          *
  1523.          * @since 140422 First documented version.
  1524.          *
  1525.          * @return boolean `TRUE` if yes; else `FALSE`.
  1526.          *
  1527.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1528.          */
  1529.         public function is_ssl()
  1530.         {
  1531.             static $is; // Cache.
  1532.             if(isset($is)) return $is;
  1533.  
  1534.             if(!empty($_SERVER['SERVER_PORT']))
  1535.                 if($_SERVER['SERVER_PORT'] === '443')
  1536.                     return ($is = TRUE);
  1537.  
  1538.             if(!empty($_SERVER['HTTPS']))
  1539.                 if($_SERVER['HTTPS'] === '1' || strcasecmp($_SERVER['HTTPS'], 'on') === 0)
  1540.                     return ($is = TRUE);
  1541.  
  1542.             if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
  1543.                 if(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0)
  1544.                     return ($is = TRUE);
  1545.  
  1546.             return ($is = FALSE);
  1547.         }
  1548.  
  1549.         /**
  1550.          * Is a document/string an HTML/XML doc; or no?
  1551.          *
  1552.          * @since 140422 First documented version.
  1553.          *
  1554.          * @param string $doc Input string/document to check.
  1555.          *
  1556.          * @return boolean `TRUE` if yes; else `FALSE`.
  1557.          */
  1558.         public function is_html_xml_doc($doc)
  1559.         {
  1560.             if(($doc = (string)$doc))
  1561.                 if(stripos($doc, '</html>') !== FALSE || stripos($doc, '<?xml') === 0)
  1562.                     return TRUE;
  1563.             return FALSE; // Not an HTML/XML document.
  1564.         }
  1565.  
  1566.         /**
  1567.          * Does the current request have a cacheable content type?
  1568.          *
  1569.          * @since 140422 First documented version.
  1570.          *
  1571.          * @return boolean `TRUE` if yes; else `FALSE`.
  1572.          *
  1573.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1574.          */
  1575.         public function has_a_cacheable_content_type()
  1576.         {
  1577.             static $has; // Cache.
  1578.             if(isset($has)) return $has;
  1579.  
  1580.             foreach(headers_list() as $_header)
  1581.                 if(stripos($_header, 'Content-Type:') === 0)
  1582.                     $content_type = $_header; // Last one.
  1583.             unset($_header); // Just a little housekeeping.
  1584.  
  1585.             if(isset($content_type[0]) && stripos($content_type, 'html') === FALSE && stripos($content_type, 'xml') === FALSE && stripos($content_type, __NAMESPACE__) === FALSE)
  1586.                 return ($has = FALSE); // Do NOT cache data sent by scripts serving other MIME types.
  1587.  
  1588.             return ($has = TRUE); // Assume that it is by default, we are within WP after all.
  1589.         }
  1590.  
  1591.         /**
  1592.          * Does the current request have a cacheable HTTP status code?
  1593.          *
  1594.          * @since 140422 First documented version.
  1595.          *
  1596.          * @return boolean `TRUE` if yes; else `FALSE`.
  1597.          *
  1598.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1599.          */
  1600.         public function has_a_cacheable_status()
  1601.         {
  1602.             static $has; // Cache.
  1603.             if(isset($has)) return $has;
  1604.  
  1605.             if(function_exists('http_response_code') && ($http_response_code = http_response_code()))
  1606.                 $this->http_status = $http_response_code;
  1607.  
  1608.             $http_status = (string)$this->http_status; // So we can check indexes.
  1609.             if(isset($http_status[0]) && $http_status[0] !== '2' && $http_status !== '404')
  1610.                 return ($has = FALSE); // A non-2xx & non-404 status code.
  1611.             /*
  1612.              * PHP's `headers_list()` currently does NOT include `HTTP/` headers.
  1613.              *    This means the following routine will never catch a status sent by `status_header()`.
  1614.              *    However, I'm leaving this check in place in case a future version of PHP adds support for this.
  1615.              *
  1616.              *    For now, we monitor `status_header()` via {@link maybe_filter_status_header_postload()} so that will suffice.
  1617.              */
  1618.             foreach(headers_list() as $_header)
  1619.                 if(preg_match('/^(?:Retry\-After\:\s+(?P<retry>.+)|Status\:\s+(?P<status>[0-9]+)|HTTP\/[0-9]+\.[0-9]+\s+(?P<http_status>[0-9]+))/i', $_header, $_m))
  1620.                     if(!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404')
  1621.                        || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404')
  1622.                     ) return ($has = FALSE); // Don't cache (anything that's NOT a 2xx or 404 status).
  1623.             unset($_header); // Just a little housekeeping.
  1624.  
  1625.             return ($has = TRUE); // Assume that it is by default, we are within WP after all.
  1626.         }
  1627.  
  1628.         /**
  1629.          * Checks if a PHP extension is loaded up.
  1630.          *
  1631.          * @since 140422 First documented version.
  1632.          *
  1633.          * @param string $extension A PHP extension slug (i.e. extension name).
  1634.          *
  1635.          * @return boolean `TRUE` if the extension is loaded; else `FALSE`.
  1636.          *
  1637.          * @note The return value of this function is cached to reduce overhead on repeat calls.
  1638.          */
  1639.         public function is_extension_loaded($extension)
  1640.         {
  1641.             static $is = array(); // Static cache.
  1642.             if(isset($is[$extension])) return $is[$extension];
  1643.             return ($is[$extension] = extension_loaded($extension));
  1644.         }
  1645.  
  1646.         /**
  1647.          * Assigns an ID to each callable attached to a hook/filter.
  1648.          *
  1649.          * @since 140422 First documented version.
  1650.          *
  1651.          * @param string|callable|mixed $function A string or a callable.
  1652.          *
  1653.          * @return string Hook ID for the given `$function`.
  1654.          *
  1655.          * @throws \exception If the hook/function is invalid (i.e. it's not possible to generate an ID).
  1656.          */
  1657.         public function hook_id($function)
  1658.         {
  1659.             if(is_string($function))
  1660.                 return $function;
  1661.  
  1662.             if(is_object($function)) // Closure.
  1663.                 $function = array($function, '');
  1664.             else $function = (array)$function;
  1665.  
  1666.             if(is_object($function[0]))
  1667.                 return spl_object_hash($function[0]).$function[1];
  1668.  
  1669.             else if(is_string($function[0]))
  1670.                 return $function[0].'::'.$function[1];
  1671.  
  1672.             throw new \exception(__('Invalid hook.', $this->text_domain));
  1673.         }
  1674.  
  1675.         /**
  1676.          * Adds a new hook (works with both actions & filters).
  1677.          *
  1678.          * @since 140422 First documented version.
  1679.          *
  1680.          * @param string                $hook The name of a hook to attach to.
  1681.          * @param string|callable|mixed $function A string or a callable.
  1682.          * @param integer               $priority Hook priority; defaults to `10`.
  1683.          * @param integer               $accepted_args Max number of args that should be passed to the `$function`.
  1684.          *
  1685.          * @return boolean This always returns a `TRUE` value.
  1686.          */
  1687.         public function add_hook($hook, $function, $priority = 10, $accepted_args = 1)
  1688.         {
  1689.             $this->hooks[$hook][$priority][$this->hook_id($function)]
  1690.                 = array('function' => $function, 'accepted_args' => (integer)$accepted_args);
  1691.             return TRUE; // Always returns true.
  1692.         }
  1693.  
  1694.         /**
  1695.          * Adds a new action hook.
  1696.          *
  1697.          * @since 140422 First documented version.
  1698.          *
  1699.          * @return boolean This always returns a `TRUE` value.
  1700.          *
  1701.          * @see add_hook()
  1702.          */
  1703.         public function add_action() // Simple `add_hook()` alias.
  1704.         {
  1705.             return call_user_func_array(array($this, 'add_hook'), func_get_args());
  1706.         }
  1707.  
  1708.         /**
  1709.          * Adds a new filter.
  1710.          *
  1711.          * @since 140422 First documented version.
  1712.          *
  1713.          * @return boolean This always returns a `TRUE` value.
  1714.          *
  1715.          * @see add_hook()
  1716.          */
  1717.         public function add_filter() // Simple `add_hook()` alias.
  1718.         {
  1719.             return call_user_func_array(array($this, 'add_hook'), func_get_args());
  1720.         }
  1721.  
  1722.         /**
  1723.          * Removes a hook (works with both actions & filters).
  1724.          *
  1725.          * @since 140422 First documented version.
  1726.          *
  1727.          * @param string                $hook The name of a hook to remove.
  1728.          * @param string|callable|mixed $function A string or a callable.
  1729.          * @param integer               $priority Hook priority; defaults to `10`.
  1730.          *
  1731.          * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
  1732.          */
  1733.         public function remove_hook($hook, $function, $priority = 10)
  1734.         {
  1735.             if(!isset($this->hooks[$hook][$priority][$this->hook_id($function)]))
  1736.                 return FALSE; // Nothing to remove in this case.
  1737.  
  1738.             unset($this->hooks[$hook][$priority][$this->hook_id($function)]);
  1739.             if(!$this->hooks[$hook][$priority]) unset($this->hooks[$hook][$priority]);
  1740.             return TRUE; // Existed before it was removed in this case.
  1741.         }
  1742.  
  1743.         /**
  1744.          * Removes an action.
  1745.          *
  1746.          * @since 140422 First documented version.
  1747.          *
  1748.          * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
  1749.          *
  1750.          * @see remove_hook()
  1751.          */
  1752.         public function remove_action() // Simple `remove_hook()` alias.
  1753.         {
  1754.             return call_user_func_array(array($this, 'remove_hook'), func_get_args());
  1755.         }
  1756.  
  1757.         /**
  1758.          * Removes a filter.
  1759.          *
  1760.          * @since 140422 First documented version.
  1761.          *
  1762.          * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason.
  1763.          *
  1764.          * @see remove_hook()
  1765.          */
  1766.         public function remove_filter() // Simple `remove_hook()` alias.
  1767.         {
  1768.             return call_user_func_array(array($this, 'remove_hook'), func_get_args());
  1769.         }
  1770.  
  1771.         /**
  1772.          * Runs any callables attached to an action.
  1773.          *
  1774.          * @since 140422 First documented version.
  1775.          */
  1776.         public function do_action($hook)
  1777.         {
  1778.             if(empty($this->hooks[$hook]))
  1779.                 return; // No hooks.
  1780.  
  1781.             $hook_actions = $this->hooks[$hook];
  1782.             ksort($hook_actions); // Sort by priority.
  1783.  
  1784.             $args = func_get_args(); // We'll need these below.
  1785.             foreach($hook_actions as $_hook_action) foreach($_hook_action as $_action)
  1786.             {
  1787.                 if(!isset($_action['function'], $_action['accepted_args']))
  1788.                     continue; // Not a valid filter in this case.
  1789.  
  1790.                 call_user_func_array($_action['function'], array_slice($args, 1, $_action['accepted_args']));
  1791.             }
  1792.             unset($_hook_action, $_action); // Housekeeping.
  1793.         }
  1794.  
  1795.         /**
  1796.          * Runs any callables attached to a filter.
  1797.          *
  1798.          * @since 140422 First documented version.
  1799.          *
  1800.          * @param string $hook The name of a filter hook.
  1801.          * @param mixed  $value The value to filter.
  1802.          *
  1803.          * @return mixed The filtered `$value`.
  1804.          */
  1805.         public function apply_filters($hook, $value)
  1806.         {
  1807.             if(empty($this->hooks[$hook]))
  1808.                 return $value; // No hooks.
  1809.  
  1810.             $hook_filters = $this->hooks[$hook];
  1811.             ksort($hook_filters); // Sort by priority.
  1812.  
  1813.             $args = func_get_args(); // We'll need these below.
  1814.             foreach($hook_filters as $_hook_filter) foreach($_hook_filter as $_filter)
  1815.             {
  1816.                 if(!isset($_filter['function'], $_filter['accepted_args']))
  1817.                     continue; // Not a valid filter in this case.
  1818.  
  1819.                 $args[1] = $value; // Continously update the argument `$value`.
  1820.                 $value   = call_user_func_array($_filter['function'], array_slice($args, 1, $_filter['accepted_args']));
  1821.             }
  1822.             unset($_hook_filter, $_filter); // Housekeeping.
  1823.  
  1824.             return $value; // With applied filters.
  1825.         }
  1826.  
  1827.         /**
  1828.          * Apache `.htaccess` rules that deny public access to the contents of a directory.
  1829.          *
  1830.          * @since 140422 First documented version.
  1831.          *
  1832.          * @var string `.htaccess` fules.
  1833.          */
  1834.         public $htaccess_deny = "<IfModule authz_core_module>\n\tRequire all denied\n</IfModule>\n<IfModule !authz_core_module>\n\tdeny from all\n</IfModule>";
  1835.     }
  1836.  
  1837.     /**
  1838.      * Polyfill for {@link \__()}.
  1839.      *
  1840.      * @since 140422 First documented version.
  1841.      *
  1842.      * @param string $string String to translate.
  1843.      * @param string $text_domain Plugin text domain.
  1844.      *
  1845.      * @return string Possibly translated string.
  1846.      */
  1847.     function __($string, $text_domain) // Polyfill `\__()`.
  1848.     {
  1849.         static $__exists; // Static cache.
  1850.  
  1851.         if(($__exists || function_exists('__')) && ($__exists = TRUE))
  1852.             return \__($string, $text_domain);
  1853.  
  1854.         return $string; // Not possible (yet).
  1855.     }
  1856.  
  1857.     /**
  1858.      * Global Quick Cache {@link advanced_cache} instance.
  1859.      *
  1860.      * @since 140422 First documented version.
  1861.      *
  1862.      * @var advanced_cache Global instance reference.
  1863.      */
  1864.     $GLOBALS[__NAMESPACE__.'__advanced_cache'] = new advanced_cache();
  1865. }
  1866. namespace // Global namespace.
  1867. {
  1868.     /**
  1869.      * Postload event handler; overrides core WP function.
  1870.      *
  1871.      * @since 140422 First documented version.
  1872.      *
  1873.      * @note See `/wp-settings.php` around line #226.
  1874.      */
  1875.     function wp_cache_postload() // See: `wp-settings.php`.
  1876.     {
  1877.         $advanced_cache = $GLOBALS['quick_cache__advanced_cache'];
  1878.         /** @var $advanced_cache \quick_cache\advanced_cache */
  1879.         if(!$advanced_cache->is_running) return;
  1880.  
  1881.         if(!empty($advanced_cache->postload['filter_status_header']))
  1882.             $advanced_cache->maybe_filter_status_header_postload();
  1883.  
  1884.         if(!empty($advanced_cache->postload['set_debug_info']))
  1885.             $advanced_cache->maybe_set_debug_info_postload();
  1886.  
  1887.         if(!empty($advanced_cache->postload['wp_main_query']))
  1888.             add_action('wp', array($advanced_cache, 'wp_main_query_postload'), PHP_INT_MAX);
  1889.     }
  1890. }
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement