├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── readme.txt ├── uninstall.php ├── wp-spider-cache.php └── wp-spider-cache ├── assets ├── css │ └── spider-cache.css ├── img │ ├── spinner-2x.gif │ └── spinner.gif └── js │ └── spider-cache.js ├── drop-ins ├── advanced-cache.php └── object-cache.php └── includes ├── class-object-base.php ├── class-object-cache.php ├── class-object-memcached.php ├── class-object-memory.php ├── class-object-redis.php ├── class-output-cache.php ├── class-var-dump.php └── functions.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [6.0.1] - 2021-05-29 2 | * Update author info 3 | 4 | ## [6.0.0] - 2017-05-20 5 | * UI for copying drop-ins 6 | 7 | ## [5.0.0] - 2017-04-03 8 | * Updated signup and signupmeta global cache groups 9 | 10 | ## [4.1.0] - 2016-11-20 11 | * General code improvements 12 | 13 | ## [4.0.0] - 2016-10-24 14 | * Add support for global LudicrousDB cache group 15 | 16 | ## [3.4.0] - 2016-10-18 17 | * Fix key & group deletion from UI 18 | * Move BuddyPress cache-groups to root site of network 19 | 20 | ## [3.3.0] - 2016-10-07 21 | * Prevent fatal errors on WordPress 4.7 22 | * Bump minimum WordPress version to 4.7 23 | 24 | ## 3.2.0 (2016-09-22) 25 | * Prevent fatals if packaged drop-ins are not used 26 | * Prevent fatal errors if supported back-ends are not installed 27 | 28 | ## 3.0.3 (2016-08-22) 29 | * Yield to XDebug if enabled 30 | * Improve output of pretty var_dump 31 | 32 | ## 3.0.2 (2016-08-22) 33 | * Use correct callback functions 34 | 35 | ## 3.0.1 (2016-08-21) 36 | * Fix bug relating to Thickbox refresh 37 | 38 | ## 3.0.0 (2016-08-21) 39 | * Improved cache view using Thickbox 40 | 41 | ## 2.2.1 (2016-07-29) 42 | * Asset bump 43 | 44 | ## 2.2.0 (2016-07-29) 45 | * Refactoring throughout 46 | * Adding caps 47 | * Cache exempt cookie 48 | 49 | ## 2.1.1 (2016-02-15) 50 | * Sanity checks for Memcached & drop-ins 51 | 52 | ## 2.1.0 (2016-02-15) 53 | * Refactor drop-ins 54 | * More accurate debug times 55 | * More protective method scopes 56 | * Better output cache encapsulation 57 | * Rename a few old functions 58 | 59 | ## 2.0.0 (2016-02-15) 60 | * Initial release 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WP Spider Cache 2 | 3 | WP Spider Cache is your friendly neighborhood caching solution for WordPress. It uses Memcached to store both objects & page output. 4 | 5 | If you are familiar with Batcache and WP Super Cache, you'll be right at home here. 6 | 7 | It comes with: 8 | * Support for registering multiple Memcached backend servers 9 | * An administration interface for viewing cache data 10 | * Ability to flush specific keys & groups 11 | * Drop-in plugins for `object-cache.php` & `advanced-cache.php` 12 | 13 | # Installation 14 | 15 | * Download and install using the built in WordPress plugin installer. 16 | * Copy contents of `drop-ins` to your `wp-content` directory 17 | * Activate in the "Plugins" area of your `wp-admin` by clicking the "Activate" link. 18 | * Consider sponsoring future development by clicking "Sponsor". 19 | * No further setup or configuration is necessary. 20 | 21 | # FAQ 22 | 23 | ### Does this work with on single-site, multi-site, and multi-network installations? 24 | 25 | Yes. Yes. Yes. 26 | 27 | ### Does this work with BuddyPress, bbPress, and GlotPress? 28 | 29 | Yes. Yes. Yes. 30 | 31 | ### What other plugins has this been tested with? 32 | 33 | * EasyDigitalDownloads 34 | * WooCommerce 35 | * Jetpack 36 | * All Stuttter plugins 37 | * Keyring 38 | * User Switching 39 | 40 | ### Credits 41 | 42 | This plugin is largely inspired by: 43 | 44 | * Memcached 45 | * Batcache 46 | * Super Cache 47 | * Johnny Cache 48 | 49 | ### Where can I get support? 50 | 51 | This plugin is free for anyone to use. 52 | 53 | * Community: https://wordpress.org/support/plugin/wp-spider-cache 54 | * Development: https://github.com/stuttter/wp-spider-cache/discussions 55 | 56 | If you require immediate assistance, please consider a paid support subscription. 57 | 58 | ### Contributing 59 | 60 | Please [open a new issue](/pull/new/master) to discuss whether the feature is a good fit for the project. Once you've decided to work on a pull request, please follow the [WordPress Coding Standards](http://make.wordpress.org/core/handbook/coding-standards/). 61 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stuttter/wp-spider-cache", 3 | "description": "Your friendly neighborhood caching solution for WordPress", 4 | "homepage": "https://github.com/stuttter/wp-spider-cache", 5 | "type": "wordpress-plugin", 6 | "require": { 7 | "php": ">=5.2", 8 | "composer/installers": "^1.0" 9 | }, 10 | "license": "GPL-2.0-or-later", 11 | "authors": [ 12 | { 13 | "name": "John James Jacoby", 14 | "email": "johnjamesjacoby@me.com" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WP Spider Cache === 2 | Author: Triple J Software, Inc. 3 | Author URI: https://jjj.software 4 | Donate link: https://buy.stripe.com/7sI3cd2tK1Cy2lydQR 5 | Plugin URI: https://wordpress.org/plugins/wp-spider-cache 6 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 7 | License: GPLv2 or later 8 | Contributors: johnjamesjacoby 9 | Tags: cache, memcache, memcached, object, output 10 | Requires PHP: 7.2 11 | Requires at least: 5.2 12 | Tested up to: 5.8 13 | Stable tag: 6.0.1 14 | 15 | == Description == 16 | 17 | WP Spider Cache is your friendly neighborhood caching solution for WordPress. It uses Memcached to store both objects & page output. 18 | 19 | It is heroic like Batcache & Super Cache, but younger, faster, and a natural web-slinger. 20 | 21 | It comes with: 22 | 23 | * Support for registering multiple cache servers 24 | * An administration interface for viewing cache data 25 | * Ability to flush specific keys & groups 26 | * Drop-in plugins for `object-cache.php` & `advanced-cache.php` 27 | 28 | = Also checkout = 29 | 30 | Admin: 31 | 32 | * [WP Chosen](https://wordpress.org/plugins/wp-chosen/ "Make long, unwieldy select boxes much more user-friendly.") 33 | * [WP Comment Humility](https://wordpress.org/plugins/wp-comment-humility/ "Move the "Comments" menu underneath 'Posts'.") 34 | * [WP Pretty Filters](https://wordpress.org/plugins/wp-pretty-filters/ "Make all filters match the Media & Attachments interface.") 35 | * [WP Reset Filters](https://wordpress.org/plugins/wp-reset-filters/ "Adds a "Reset" button to all admin area filters.") 36 | 37 | Media: 38 | 39 | * [WP Media Categories](https://wordpress.org/plugins/wp-media-categories/ "Add categories to media & attachments.") 40 | 41 | Multisite: 42 | 43 | * [WP Multi Network](https://wordpress.org/plugins/wp-multi-network/ "Create many networks of many sites with any domains.") 44 | * [WP Site Aliases](https://wordpress.org/plugins/wp-site-aliases/ "Create many networks of many sites with any domains.") 45 | 46 | Posts: 47 | 48 | * [WP Article Order](https://wordpress.org/plugins/wp-article-order/ "Move articles to the end of post titles.") 49 | 50 | System: 51 | 52 | * [LudicrousDB](https://wordpress.org/plugins/ludicrousdb/ "Minifies & concatenates enqueued scripts & styles.") 53 | * [WP Enqueue Masher](https://wordpress.org/plugins/wp-enqueue-masher/ "Minifies & concatenates enqueued scripts & styles.") 54 | * [WP Spider Cache](https://wordpress.org/plugins/wp-spider-cache/ "Your friendly neighborhood caching solution for WordPress.") 55 | 56 | Terms: 57 | 58 | * [WP Term Authors](https://wordpress.org/plugins/wp-term-authors/ "Authors for categories, tags, and other taxonomy terms.") 59 | * [WP Term Colors](https://wordpress.org/plugins/wp-term-colors/ "Pretty colors for categories, tags, and other taxonomy terms.") 60 | * [WP Term Families](https://wordpress.org/plugins/wp-term-families/ "Associate taxonomy terms with other taxonomy terms.") 61 | * [WP Term Icons](https://wordpress.org/plugins/wp-term-icons/ "Pretty icons for categories, tags, and other taxonomy terms.") 62 | * [WP Term Images](https://wordpress.org/plugins/wp-term-images/ "Pretty images for categories, tags, and other taxonomy terms.") 63 | * [WP Term Locks](https://wordpress.org/plugins/wp-term-locks/ "Protect categories, tags, and other taxonomy terms from being edited or deleted.") 64 | * [WP Term Order](https://wordpress.org/plugins/wp-term-order/ "Sort taxonomy terms, your way.") 65 | * [WP Term Visibility](https://wordpress.org/plugins/wp-term-visibility/ "Visibilities for categories, tags, and other taxonomy terms.") 66 | 67 | Users: 68 | 69 | * [WP User Activity](https://wordpress.org/plugins/wp-user-activity/ "The best way to log activity in WordPress.") 70 | * [WP User Alerts](https://wordpress.org/plugins/wp-user-alerts/ "Send alerts to users when new posts are published.") 71 | * [WP User Avatars](https://wordpress.org/plugins/wp-user-avatars/ "Allow users to upload avatars or choose them from your media library.") 72 | * [WP User Groups](https://wordpress.org/plugins/wp-user-groups/ "Group users together with taxonomies & terms.") 73 | * [WP User Parents](https://wordpress.org/plugins/wp-user-parents/ "A user hierarchy for WordPress user accounts.") 74 | * [WP User Preferences](https://wordpress.org/plugins/wp-user-preferences/ "Cascading user options with intelligent defaults.") 75 | * [WP User Profiles](https://wordpress.org/plugins/wp-user-profiles/ "A sophisticated way to edit users in WordPress.") 76 | * [WP User Signups](https://wordpress.org/plugins/wp-user-signups/ "An interface for managing user signups.") 77 | * [WP User Tagline](https://wordpress.org/plugins/wp-user-tagline/ "Allow users to give themselves unique taglines.") 78 | * [WP User Title](https://wordpress.org/plugins/wp-user-title/ "Allow users to give themselves unique titles.") 79 | 80 | = Credits = 81 | 82 | This plugin is largely inspired by: 83 | 84 | * Memcached 85 | * Batcache 86 | * Super Cache 87 | * Johnny Cache 88 | 89 | == Screenshots == 90 | 91 | 1. Admin UI 92 | 2. Servers 93 | 3. Data View 94 | 95 | == Installation == 96 | 97 | * Download and install using the built in WordPress plugin installer. 98 | * Copy contents of `drop-ins` to your `wp-content` directory 99 | * Activate in the "Plugins" area of your admin by clicking the "Activate" link. 100 | * No further setup or configuration is necessary. 101 | 102 | == Frequently Asked Questions == 103 | 104 | = Does this work with on single-site, multi-site, and multi-network installations? = 105 | 106 | Yes. Yes. Yes. 107 | 108 | = Does this work with BuddyPress, bbPress, and GlotPress? = 109 | 110 | Yes. Yes. Yes. 111 | 112 | = What other plugins has this been tested with? = 113 | 114 | * Easy Digital Downloads 115 | * Jetpack 116 | * Keyring 117 | * User Switching 118 | * WooCommerce 119 | 120 | = Where can I get support? = 121 | 122 | * Community: https://wordpress.org/support/plugin/wp-spider-cache 123 | * Development: https://github.com/stuttter/wp-spider-cache/discussions 124 | 125 | == Changelog == 126 | 127 | = [6.0.1] - 2021-05-29 = 128 | * Update author info 129 | 130 | = [6.0.0] - 2017-05-20 = 131 | * UI for copying drop-ins 132 | 133 | = [5.0.0] - 2017-04-03 = 134 | * Updated signup and signupmeta global cache groups 135 | 136 | = [4.1.0] - 2016-11-20 = 137 | * General code improvements 138 | 139 | = [4.0.0] - 2016-10-24 = 140 | * Add support for global LudicrousDB cache group 141 | 142 | = [3.4.0] - 2016-10-18 = 143 | * Fix key & group deletion from UI 144 | * Move BuddyPress cache-groups to root site of network 145 | 146 | = [3.3.0] - 2016-10-07 = 147 | * Prevent fatal errors on WordPress 4.7 148 | * Bump minimum WordPress version to 4.7 149 | 150 | = [3.2.0] - 2016-09-22 = 151 | * Prevent fatal errors if packaged drop-ins are not used 152 | * Prevent fatal errors if supported back-ends are not installed 153 | 154 | = [3.1.0] - 2016-09-08 = 155 | * Add extended global cache groups 156 | 157 | = [3.0.3] - 2016-08-22 = 158 | * Yield to XDebug if enabled 159 | * Improve output of pretty var_dump 160 | 161 | = [3.0.2] - 2016-08-22 = 162 | * Use correct callback functions 163 | 164 | = [3.0.1] - 2016-08-21 = 165 | * Fix bug relating to Thickbox refresh 166 | 167 | = [3.0.0] - 2016-08-21 = 168 | * Improved cache view using Thickbox 169 | 170 | = [2.2.1] - 2016-07-29 = 171 | * Asset bump 172 | 173 | = [2.2.0] - 2016-07-29 = 174 | * Refactoring throughout 175 | * Adding caps 176 | * Cache exempt cookie 177 | 178 | = [2.1.1] - 2016-02-15 = 179 | * Sanity checks for Memcached & drop-ins 180 | 181 | = [2.1.0] - 2016-02-15 = 182 | * Refactor drop-ins 183 | * More accurate debug times 184 | * More protective method scopes 185 | * Better output cache encapsulation 186 | * Rename a few old functions 187 | 188 | = [2.0.0] - 2016-02-15 = 189 | * Initial release 190 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | delete( WP_CONTENT_DIR . '/object-cache.php' ); 20 | } 21 | } 22 | 23 | // Output cache 24 | if ( file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) ) { 25 | 26 | // Flush the entire cache before procedeing 27 | wp_cache_flush(); 28 | 29 | // Delete 30 | if ( WP_Filesystem( request_filesystem_credentials( '' ) ) ) { 31 | $wp_filesystem->delete( WP_CONTENT_DIR . '/advanced-cache.php' ); 32 | } 33 | } 34 | 35 | // End & clean the buffer 36 | ob_end_clean(); 37 | -------------------------------------------------------------------------------- /wp-spider-cache.php: -------------------------------------------------------------------------------- 1 | hook = add_menu_page( 193 | esc_html__( 'Spider Cache', 'wp-spider-cache' ), 194 | esc_html__( 'Spider Cache', 'wp-spider-cache' ), 195 | 'manage_cache', // Single-site admins and multi-site super admins 196 | 'wp-spider-cache', 197 | array( $this, 'page' ), 198 | 'dashicons-editor-code' 199 | ); 200 | 201 | // Load page on hook 202 | add_action( "load-{$this->hook}", array( $this, 'load' ) ); 203 | add_action( "load-{$this->hook}", array( $this, 'help' ) ); 204 | } 205 | 206 | /** 207 | * Enqueue assets 208 | * 209 | * @since 2.0.0 210 | */ 211 | public function admin_enqueue() { 212 | 213 | // Bail if not this page 214 | if ( $GLOBALS['page_hook'] !== $this->hook ) { 215 | return; 216 | } 217 | 218 | // Setup the plugin URL, for enqueues 219 | $this->asset_url = plugin_dir_url( __FILE__ ) . 'wp-spider-cache/'; 220 | 221 | // Enqueue 222 | wp_enqueue_style( 'wp-spider-cache', $this->asset_url . 'assets/css/spider-cache.css', array(), $this->asset_version ); 223 | wp_enqueue_script( 'wp-spider-cache', $this->asset_url . 'assets/js/spider-cache.js', array( 'jquery' ), $this->asset_version, true ); 224 | 225 | // Localize JS 226 | wp_localize_script( 'wp-spider-cache', 'WP_Spider_Cache', array( 227 | 'no_results' => $this->get_no_results_row(), 228 | 'refreshing_results' => $this->get_refreshing_results_row() 229 | ) ); 230 | } 231 | 232 | /** 233 | * Map `manage_cache` capability 234 | * 235 | * @since 2.3.0 236 | * 237 | * @param array $caps 238 | * @param string $cap 239 | */ 240 | public function map_meta_cap( $caps = array(), $cap = '' ) { 241 | 242 | // Map single-site cap check to 'manage_options' 243 | if ( 'manage_cache' === $cap ) { 244 | if ( ! is_multisite() ) { 245 | $caps = array( 'manage_options' ); 246 | } 247 | } 248 | 249 | // Return maybe-mapped caps 250 | return $caps; 251 | } 252 | 253 | /** 254 | * Maybe copy a drop-in plugin based on user request 255 | * 256 | * @since 6.0.0 257 | * 258 | * @param bool $redirect 259 | */ 260 | private function maybe_copy_drop_in( $redirect = true ) { 261 | 262 | // Bail if not copying or missing nonce 263 | if ( empty( $_GET['copy'] ) || empty( $_GET['nonce'] ) ) { 264 | return; 265 | } 266 | 267 | // Sanitize action 268 | $which = sanitize_key( $_GET['copy'] ); 269 | 270 | // Check which to copy, or bail 271 | if ( 'object' === $which ) { 272 | $dest = WP_CONTENT_DIR . '/object-cache.php'; 273 | $source = plugin_dir_path( __FILE__ ) . 'wp-spider-cache/drop-ins/object-cache.php'; 274 | } elseif ( 'output' === $which ) { 275 | $dest = WP_CONTENT_DIR . '/advanced-cache.php'; 276 | $source = plugin_dir_path( __FILE__ ) . 'wp-spider-cache/drop-ins/advanced-cache.php'; 277 | } else { 278 | return; 279 | } 280 | 281 | // Bail if under version control 282 | if ( true === $this->vcs ) { 283 | return; 284 | } 285 | 286 | // Try to copy 287 | if ( wp_verify_nonce( $_GET['nonce'], self::FILE_NONCE ) ) { 288 | $copied = copy( $source, $dest ) 289 | ? 'success' 290 | : 'failed'; 291 | } else { 292 | $copied = 'failed'; 293 | } 294 | 295 | // Bail if no redirect 296 | if ( false === $redirect ) { 297 | return; 298 | } 299 | 300 | // Assemble the URL 301 | $url = add_query_arg( array( 302 | 'copied' => $copied, 303 | ), menu_page_url( 'wp-spider-cache', false ) ); 304 | 305 | // Redirect 306 | wp_safe_redirect( $url ); 307 | exit(); 308 | } 309 | 310 | /** 311 | * Maybe clear a cache group, based on user request 312 | * 313 | * @since 2.0.0 314 | * 315 | * @param bool $redirect 316 | */ 317 | private function maybe_clear_cache_group( $redirect = true ) { 318 | 319 | // Bail if not clearing 320 | if ( empty( $_GET['cache_group'] ) ) { 321 | return; 322 | } 323 | 324 | // Sanitize cache group to clear 325 | $group = $this->sanitize_key( $_GET['cache_group'] ); 326 | 327 | // Clear the cache group 328 | $cleared = $this->clear_group( $group ); 329 | 330 | // Bail if not redirecting 331 | if ( false === $redirect ) { 332 | return; 333 | } 334 | 335 | // Assemble the URL 336 | $url = add_query_arg( array( 337 | 'type' => 'group', 338 | 'keys_cleared' => $cleared, 339 | 'cache_cleared' => $group 340 | ), menu_page_url( 'wp-spider-cache', false ) ); 341 | 342 | // Redirect 343 | wp_safe_redirect( $url ); 344 | exit(); 345 | } 346 | 347 | /** 348 | * Maybe clear a user's entire cache, based on user request 349 | * 350 | * @since 2.0.0 351 | * 352 | * @param bool $redirect 353 | */ 354 | private function maybe_clear_user_cache( $redirect = true ) { 355 | 356 | // Clear user ID 357 | if ( empty( $_GET['user_id'] ) ) { 358 | return; 359 | } 360 | 361 | // How are we getting the user? 362 | if ( is_numeric( $_GET['user_id'] ) ) { 363 | $by = 'id'; 364 | } elseif ( is_email( $_GET['user_id'] ) ) { 365 | $by = 'email'; 366 | } elseif ( is_string( $_GET['user_id'] ) ) { 367 | $by = 'slug'; 368 | } else { 369 | $by = 'login'; 370 | } 371 | 372 | // Get the user 373 | $_user = get_user_by( $by, $_GET['user_id'] ); 374 | 375 | // Bail if no user found 376 | if ( empty( $_user ) ) { 377 | return; 378 | } 379 | 380 | $cleared = array(); 381 | 382 | // Delete user caches 383 | $cleared[] = wp_cache_delete( $_user->ID, 'users' ); 384 | $cleared[] = wp_cache_delete( $_user->ID, 'usermeta' ); 385 | $cleared[] = wp_cache_delete( $_user->ID, 'user_meta' ); 386 | $cleared[] = wp_cache_delete( $_user->user_login, 'userlogins' ); 387 | $cleared[] = wp_cache_delete( $_user->user_nicename, 'userslugs' ); 388 | $cleared[] = wp_cache_delete( $_user->user_email, 'useremail' ); 389 | $cleared[] = wp_cache_delete( $_user->user_email, 'signups' ); 390 | $cleared[] = wp_cache_delete( $_user->user_email, 'signup_meta' ); 391 | 392 | // Bail if not redirecting 393 | if ( false === $redirect ) { 394 | return; 395 | } 396 | 397 | // Assemble the URL 398 | $url = add_query_arg( array( 399 | 'type' => 'user', 400 | 'keys_cleared' => count( array_filter( $cleared ) ), 401 | 'cache_cleared' => $_user->ID 402 | ), menu_page_url( 'wp-spider-cache', false ) ); 403 | 404 | // Redirect 405 | wp_safe_redirect( $url ); 406 | exit(); 407 | } 408 | 409 | /** 410 | * Check for network activation and init to add menu item. 411 | * 412 | * @since 2.2.0 413 | */ 414 | public function set_blog_ids() { 415 | 416 | // Sanitize type 417 | $type = ! empty( $_POST['type'] ) 418 | ? sanitize_key( $_POST['type'] ) 419 | : ''; 420 | 421 | // Set blog IDs 422 | switch ( $type ) { 423 | case 'user' : 424 | case 'network' : 425 | $this->blog_ids = array( 0 ); 426 | break; 427 | case 'blog' : 428 | case '' : 429 | $this->blog_ids = array( get_current_blog_id() ); 430 | break; 431 | } 432 | } 433 | 434 | /** 435 | * Custom cache key separator 436 | * 437 | * @since 5.0.0 438 | */ 439 | public function set_separator() { 440 | 441 | // Use from object cache 442 | $this->cache_key_separator = function_exists( 'wp_object_cache' ) 443 | ? wp_object_cache()->cache_key_separator 444 | : ':'; 445 | 446 | // Fallback support, incase it's empty 447 | if ( empty( $this->cache_key_separator ) ) { 448 | $this->cache_key_separator = ':'; 449 | } 450 | } 451 | 452 | /** 453 | * Helper function to check nonce and avoid caching the request 454 | * 455 | * @since 2.0.0 456 | * 457 | * @param string $nonce 458 | */ 459 | private function check_nonce( $nonce = '' ) { 460 | check_ajax_referer( $nonce , 'nonce' ); 461 | 462 | nocache_headers(); 463 | } 464 | 465 | /** 466 | * Attempt to output the server cache contents 467 | * 468 | * @since 2.0.0 469 | */ 470 | public function ajax_get_instance() { 471 | $this->check_nonce( self::INSTANCE_NONCE ); 472 | 473 | // Attempt to output the server contents 474 | if ( empty( $_POST['name'] ) ) { 475 | wp_die( -1 ); 476 | } 477 | 478 | // Get memcache data 479 | $server = filter_var( $_POST['name'], FILTER_VALIDATE_IP ); 480 | $this->set_blog_ids(); 481 | $this->set_separator(); 482 | $this->do_rows( $server ); 483 | 484 | wp_die(); 485 | } 486 | 487 | /** 488 | * Delete all cache keys in a cache group 489 | * 490 | * @since 2.0.0 491 | */ 492 | public function ajax_flush_group() { 493 | $this->check_nonce( self::FLUSH_NONCE ); 494 | 495 | // Bail if missing keys or group 496 | if ( empty( $_POST['keys'] ) || empty( $_GET['group'] ) ) { 497 | wp_die( $_POST ); 498 | } 499 | 500 | // Decode group 501 | $g_code = base64_decode( $_GET['group'] ); 502 | $keys = array(); 503 | 504 | // Loop through ajax posted keys and attempt to delete them 505 | foreach ( $_POST['keys'] as $key ) { 506 | 507 | // Decode key 508 | $k_code = base64_decode( $key ); 509 | 510 | // Deleted 511 | $deleted = wp_cache_delete( 512 | $this->sanitize_key( $k_code ), 513 | $this->sanitize_key( $g_code ) 514 | ); 515 | 516 | $keys[] = array( 517 | 'group' => $g_code, 518 | 'id' => $k_code, 519 | 'code' => $key, 520 | 'result' => $deleted 521 | ); 522 | } 523 | 524 | // Pass keys 525 | wp_die( json_encode( array( 526 | 'success' => ! empty( $keys ), 527 | 'keys' => $keys 528 | ) ) ); 529 | } 530 | 531 | /** 532 | * Delete a single cache key in a specific group 533 | * 534 | * @since 2.0.0 535 | */ 536 | public function ajax_remove_item() { 537 | $this->check_nonce( self::REMOVE_NONCE ); 538 | 539 | // Delete a key in a group 540 | if ( empty( $_GET['key'] ) || empty( $_GET['group'] ) ) { 541 | wp_die( -1 ); 542 | } 543 | 544 | // Decode 545 | $k_code = base64_decode( $_GET['key'] ); 546 | $g_code = base64_decode( $_GET['group'] ); 547 | 548 | // Delete cache 549 | $deleted = wp_cache_delete( 550 | $this->sanitize_key( $k_code ), 551 | $this->sanitize_key( $g_code ) 552 | ); 553 | 554 | // Pass keys 555 | wp_die( json_encode( array( 556 | 'success' => $deleted, 557 | 'key' => $k_code, 558 | 'group' => $g_code 559 | ) ) ); 560 | } 561 | 562 | /** 563 | * Attempt to get a cached item 564 | * 565 | * @since 2.0.0 566 | */ 567 | public function ajax_get_item() { 568 | $this->check_nonce( self::GET_NONCE ); 569 | 570 | // Bail if invalid posted data 571 | if ( empty( $_GET['key'] ) || empty( $_GET['group'] ) ) { 572 | wp_die( -1 ); 573 | } 574 | 575 | // Decode 576 | $k_code = base64_decode( $_GET['key'] ); 577 | $g_code = base64_decode( $_GET['group'] ); 578 | 579 | // Get the item 580 | $this->do_item( 581 | $this->sanitize_key( $k_code ), 582 | $this->sanitize_key( $g_code ) 583 | ); 584 | 585 | wp_die(); 586 | } 587 | 588 | /** 589 | * Clear all of the items in a cache group 590 | * 591 | * @since 2.0.0 592 | * 593 | * @param string $group 594 | * @return int 595 | */ 596 | public function clear_group( $group = '' ) { 597 | 598 | // Setup counter 599 | $cleared = 0; 600 | $servers = $this->get_servers(); 601 | 602 | // Loop through servers 603 | foreach ( $servers as $server ) { 604 | $port = empty( $server[1] ) ? 11211 : $server['port']; 605 | $list = $this->retrieve_keys( $server['host'], $port ); 606 | 607 | // Loop through items 608 | foreach ( $list as $item ) { 609 | if ( strstr( $item, "{$group}:" ) ) { 610 | wp_cache_delete( $item, $group ); 611 | $cleared++; 612 | } 613 | } 614 | } 615 | 616 | // Return count 617 | return $cleared; 618 | } 619 | 620 | /** 621 | * Check for actions 622 | * 623 | * @since 2.0.0 624 | */ 625 | public function load() { 626 | 627 | // Set drop-ins state 628 | $this->drop_ins = array( 629 | 'output' => file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ), 630 | 'object' => file_exists( WP_CONTENT_DIR . '/object-cache.php' ) 631 | ); 632 | 633 | // Include the automatic updater 634 | if ( ! class_exists( 'WP_Automatic_Updater' ) ) { 635 | include ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php'; 636 | } 637 | 638 | /* 639 | * Avoid messing with VCS installs, at least for now. 640 | * Noted: this is not the ideal way to accomplish this. 641 | */ 642 | if ( class_exists( 'WP_Automatic_Updater' ) ) { 643 | $check_vcs = new WP_Automatic_Updater; 644 | $this->vcs = $check_vcs->is_vcs_checkout( __DIR__ ); 645 | } 646 | 647 | // Maybe execute user actions 648 | $this->maybe_copy_drop_in( true ); 649 | $this->maybe_clear_cache_group( true ); 650 | $this->maybe_clear_user_cache( true ); 651 | } 652 | 653 | /** 654 | * Help text 655 | * 656 | * @since 2.1.0 657 | */ 658 | public function help() { 659 | 660 | // Overview 661 | get_current_screen()->add_help_tab( array( 662 | 'id' => 'overview', 663 | 'title' => esc_html__( 'Overview', 'wp-spider-cache' ), 664 | 'content' => 665 | '

' . esc_html__( 'All the cached objects and output is listed alphabetically in Spider Cache, starting with global groups and ending with this specific site.', 'wp-spider-cache' ) . '

' . 666 | '

' . esc_html__( 'You can narrow the list by searching for specific group & key names.', 'wp-spider-cache' ) . '

' 667 | ) ); 668 | 669 | // Using cache key salt 670 | if ( defined( 'WP_CACHE_KEY_SALT' ) && WP_CACHE_KEY_SALT ) { 671 | get_current_screen()->add_help_tab( array( 672 | 'id' => 'salt', 673 | 'title' => esc_html__( 'Cache Key', 'wp-spider-cache' ), 674 | 'content' => 675 | '

' . sprintf( esc_html__( 'A Cache Key Salt was identified: %s', 'wp-spider-cache' ), '' . WP_CACHE_KEY_SALT . '' ) . '

' . 676 | '

' . __( 'This advanced configuration option is usually defined in wp-config.php and is commonly used as a way to invalidate all cached data for the entire installation by updating the value of the WP_CACHE_KEY_SALT constant.', 'wp-spider-cache' ) . '

' 677 | ) ); 678 | } 679 | 680 | // Servers 681 | get_current_screen()->add_help_tab( array( 682 | 'id' => 'servers', 683 | 'title' => esc_html__( 'Servers', 'wp-spider-cache' ), 684 | 'content' => 685 | '

' . esc_html__( 'Choose a registered cache server from the list. Content from that server is automatically retrieved & presented in the table.', 'wp-spider-cache' ) . '

' . 686 | '

' . esc_html__( 'It is possible to have more than one cache server, and each server may have different cached content available to it.', 'wp-spider-cache' ) . '

' . 687 | '

' . esc_html__( 'Clicking "Refresh" will fetch fresh data from the selected server, and repopulate the table.', 'wp-spider-cache' ) . '

' 688 | ) ); 689 | 690 | // Screen Content 691 | get_current_screen()->add_help_tab( array( 692 | 'id' => 'content', 693 | 'title' => __( 'Screen Content', 'wp-spider-cache' ), 694 | 'content' => 695 | '

' . esc_html__( 'Cached content is displayed in the following way:', 'wp-spider-cache' ) . '

' 699 | ) ); 700 | 701 | // Available actions 702 | get_current_screen()->add_help_tab( array( 703 | 'id' => 'actions', 704 | 'title' => __( 'Available Actions', 'wp-spider-cache' ), 705 | 'content' => 706 | '

' . esc_html__( 'Hovering over a row in the list will display action links that allow you to manage that content. You can perform the following actions:', 'wp-spider-cache' ) . '

' 712 | ) ); 713 | 714 | // Help Sidebar 715 | get_current_screen()->set_help_sidebar( 716 | '

' . esc_html__( 'Blog ID', 'wp-spider-cache' ) . '

' . 717 | '

' . esc_html__( 'Cache Group', 'wp-spider-cache' ) . '

' . 718 | '

' . esc_html__( 'Keys', 'wp-spider-cache' ) . '

' . 719 | '

' . esc_html__( 'Count', 'wp-spider-cache' ) . '

' 720 | ); 721 | } 722 | 723 | /** 724 | * Get all cache keys on a server 725 | * 726 | * @since 2.0.0 727 | * 728 | * @param string $server 729 | * @param int $port 730 | * 731 | * @return array 732 | */ 733 | public function retrieve_keys( $server, $port = 11211 ) { 734 | 735 | // Get slabs 736 | $list = array(); 737 | 738 | // Bail if function is missing 739 | if ( ! function_exists( 'wp_cache_get_extended_stats' ) ) { 740 | return $list; 741 | } 742 | 743 | // No errors 744 | $old_errors = error_reporting( 0 ); 745 | 746 | // Connect to cache server 747 | $connect = wp_cache_connect( $server, $port ); 748 | 749 | // Get slabs from extended stats 750 | $stats = wp_cache_get_extended_stats( 'slabs' ); 751 | 752 | // Loop through servers to get slabs 753 | foreach ( $stats as $server => $slabs ) { 754 | 755 | // Loop through slabs to target single slabs 756 | foreach ( array_keys( $slabs ) as $slab_id ) { 757 | 758 | // Skip if slab ID is empty 759 | if ( empty( $slab_id ) ) { 760 | continue; 761 | } 762 | 763 | // Get the entire slab 764 | $cache_dump = wp_cache_get_extended_stats( 'cachedump', (int) $slab_id ); 765 | 766 | // Loop through slab to find keys 767 | foreach ( $cache_dump as $slab_dump ) { 768 | 769 | // Skip if key isn't an array (how'd that happen?) 770 | if ( ! is_array( $slab_dump ) ) { 771 | continue; 772 | } 773 | 774 | // Loop through keys and add to list 775 | foreach ( array_keys( $slab_dump ) as $k ) { 776 | $list[] = $k; 777 | } 778 | } 779 | } 780 | } 781 | 782 | // Restore error reporting 783 | error_reporting( $old_errors ); 784 | 785 | // Return the list of cache server slab keys 786 | return $list; 787 | } 788 | 789 | /** 790 | * Output the contents of a cached item into a textarea 791 | * 792 | * @since 2.0.0 793 | * 794 | * @param string $key 795 | * @param string $group 796 | */ 797 | public function do_item( $key, $group ) { 798 | 799 | // Require pretty var_dump() 800 | require_once __DIR__ . '/wp-spider-cache/includes/class-var-dump.php'; 801 | 802 | // Get results directly from cache 803 | $cache = wp_cache_get( $key, $group ); 804 | $full = wp_cache_get_key( $key, $group ); 805 | $code = wp_cache_get_result_code(); 806 | $message = wp_cache_get_result_message(); 807 | 808 | // Not found? 809 | if ( false === $cache ) { 810 | $cache = 'ERR'; 811 | } ?> 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 |
837 | 838 | 'sc-flush-group', 858 | 'blog_id' => (int) $blog_id, 859 | 'group' => $this->sanitize_key( $g_key ), 860 | 'nonce' => $nonce 861 | ), admin_url( 'admin-ajax.php' ) ); 862 | 863 | // Start the output buffer 864 | ob_start(); ?> 865 | 866 | 867 | 868 | cache_key_salt ) { 899 | $offset++; 900 | } 901 | } 902 | 903 | // Increase offset for single-site 904 | if ( ! is_multisite() ) { 905 | $offset++; 906 | } 907 | 908 | // Get keys for this server and loop through them 909 | foreach ( $this->retrieve_keys( $server ) as $item ) { 910 | 911 | // Skip if CLIENT_ERROR or malforwed [sic] 912 | if ( empty( $item ) || ! strstr( $item, $this->cache_key_separator ) ) { 913 | continue; 914 | } 915 | 916 | // Separate the item into parts 917 | $parts = explode( $this->cache_key_separator, $item ); 918 | 919 | // Remove key salts 920 | if ( $offset > 0 ) { 921 | $parts = array_slice( $parts, $offset ); 922 | } 923 | 924 | // Multisite means first part is numeric 925 | if ( is_numeric( $parts[ 0 ] ) ) { 926 | $blog_id = (int) $parts[ 0 ]; 927 | $group = $parts[ 1 ]; 928 | $global = false; 929 | 930 | // Single site or global cache group 931 | } else { 932 | if ( is_multisite() ) { 933 | $blog_id = 0; 934 | $group = $parts[ 0 ]; 935 | $global = true; 936 | } else { 937 | $blog_id = (int) $GLOBALS['blog_id']; 938 | $group = $parts[ 0 ]; 939 | $global = true; 940 | } 941 | } 942 | 943 | // Only show global keys and keys for this site 944 | if ( ! in_array( $blog_id, $this->blog_ids, true ) ) { 945 | continue; 946 | } 947 | 948 | // Build the cache key based on number of parts 949 | if ( ( count( $parts ) === 1 ) ) { 950 | $key = $parts[ 0 ]; 951 | } else { 952 | if ( true === $global ) { 953 | $key = implode( $this->cache_key_separator, array_slice( $parts, 1 ) ); 954 | } else { 955 | $key = implode( $this->cache_key_separator, array_slice( $parts, 2 ) ); 956 | } 957 | } 958 | 959 | // Build group key by combining blog ID & group 960 | $group_key = $blog_id . $group; 961 | 962 | // Build the keymap 963 | if ( isset( $keymaps[ $group_key ] ) ) { 964 | $keymaps[ $group_key ]['keys'][] = $key; 965 | } else { 966 | $keymaps[ $group_key ] = array( 967 | 'blog_id' => $blog_id, 968 | 'group' => $group, 969 | 'keys' => array( $key ), 970 | 'item' => $item 971 | ); 972 | } 973 | } 974 | 975 | // Sort the keymaps by key 976 | ksort( $keymaps ); 977 | 978 | return $keymaps; 979 | } 980 | 981 | /** 982 | * Output contents of cache group keys 983 | * 984 | * @since 2.0.0 985 | * 986 | * @param int $blog_id 987 | * @param string $group 988 | * @param array $keys 989 | */ 990 | private function get_cache_key_links( $blog_id = 0, $group = '', $keys = array() ) { 991 | 992 | // Setup variables used in the loop 993 | $admin_url = admin_url( 'admin-ajax.php' ) . '#TB_inline?width=600&height=600&inlineId=sc-show-item'; 994 | 995 | // Start the output buffer 996 | ob_start(); 997 | 998 | // Loop through keys and output data & action links 999 | foreach ( $keys as $key ) : 1000 | 1001 | // Encode 1002 | $k_code = base64_encode( $key ); 1003 | $g_code = base64_encode( $group ); 1004 | 1005 | // Get URL 1006 | $get_url = add_query_arg( array( 1007 | 'blog_id' => (int) $blog_id, 1008 | 'group' => $this->sanitize_key( $g_code ), 1009 | 'key' => $this->sanitize_key( $k_code ), 1010 | 'action' => 'sc-get-item', 1011 | 'nonce' => wp_create_nonce( self::GET_NONCE ), 1012 | ), $admin_url ); 1013 | 1014 | // Remove URL 1015 | $remove_url = add_query_arg( array( 1016 | 'group' => $this->sanitize_key( $g_code ), 1017 | 'key' => $this->sanitize_key( $k_code ), 1018 | 'action' => 'sc-remove-item', 1019 | 'nonce' => wp_create_nonce( self::REMOVE_NONCE ) 1020 | ), $admin_url ); ?> 1021 | 1022 |
1023 | : ', explode( $this->cache_key_separator, $key ) ); ?> 1024 |
1025 | 1026 | 1027 | 1028 | | 1029 |
1030 |
1031 | 1032 | 1073 | 1074 |
1075 |

1076 | 1077 | 1078 | 1079 |
1080 |
1081 | 1082 | 1083 |
1084 |
1085 | 1096 | 1097 | 1098 |
1099 |
1100 | 1101 |
1102 | 1103 |
1104 |
1105 | bulk_actions(); ?> 1106 |
1107 |
1108 |
1109 | 1110 | 1111 | 1112 |
1113 |
1114 | 1115 | 1116 | 1117 |
1118 |
1119 |
1120 | 1121 | 1122 | 1123 | 1124 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | get_no_results_row(); ?> 1136 | 1137 | 1138 | 1139 | 1140 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 |
1125 | 1126 | 1127 |
1141 | 1142 | 1143 |
1150 |
1151 | 1152 | 1166 | 1167 | 1168 | 1172 | 1173 | 1174 | get_keymaps( $server ) as $values ) { 1194 | $this->do_row( $values, $nonce ); 1195 | } 1196 | } 1197 | 1198 | /** 1199 | * Output a table row based on values 1200 | * 1201 | * @since 2.0.0 1202 | * 1203 | * @param array $values 1204 | * @param string $nonce 1205 | */ 1206 | private function do_row( $values = array(), $nonce = '' ) { 1207 | ?> 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 |
get_flush_group_link( $values['blog_id'], $values['group'], $nonce ); ?>
1217 | 1218 | 1219 | 1220 | get_cache_key_links( $values['blog_id'], $values['group'], $values['keys'] ); ?> 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | '127.0.0.1', 1296 | 'port' => 11211, 1297 | 'weight' => 10 1298 | ) ); 1299 | 1300 | // Redis exists on local server 1301 | } elseif ( extension_loaded( 'Redis' ) ) { 1302 | $retval = array( array( 1303 | 'host' => '127.0.0.1', 1304 | 'port' => 6379, 1305 | 'weight' => 10 1306 | ) ); 1307 | } 1308 | 1309 | return $retval; 1310 | } 1311 | 1312 | /** 1313 | * Sanitize a user submitted cache group or key value 1314 | * 1315 | * This strips out unwanted and/or unexpected characters from cache keys 1316 | * and groups. 1317 | * 1318 | * @since 2.1.2 1319 | * 1320 | * @param string $key 1321 | * 1322 | * @return string 1323 | */ 1324 | private function sanitize_key( $key = '' ) { 1325 | return trim( $key ); 1326 | } 1327 | 1328 | /** 1329 | * Maybe output a notice to the user that action has taken place 1330 | * 1331 | * @since 2.0.0 1332 | */ 1333 | public function notice() { 1334 | 1335 | // Default status & message 1336 | $status = 'notice-warning'; 1337 | $messages = array(); 1338 | 1339 | // Bail if no notice 1340 | if ( isset( $_GET['cache_cleared'] ) ) { 1341 | 1342 | // Cleared 1343 | $keys = isset( $_GET['keys_cleared'] ) 1344 | ? (int) $_GET['keys_cleared'] 1345 | : 0; 1346 | 1347 | // Cache 1348 | $cache = isset( $_GET['cache_cleared'] ) 1349 | ? $_GET['cache_cleared'] 1350 | : 'none returned'; 1351 | 1352 | // Type 1353 | $type = isset( $_GET['type'] ) 1354 | ? sanitize_key( $_GET['type'] ) 1355 | : 'none'; 1356 | 1357 | // Success 1358 | $status = 'notice-success'; 1359 | 1360 | // Assemble the message 1361 | if ( 'group' === $type ) { 1362 | $messages[] = sprintf( 1363 | _n( 'Cleared %s key from cache group: %s', 'Cleared %s keys from cache group: %s', $keys, 'wp-spider-cache' ), 1364 | '' . esc_html( $keys ) . '', 1365 | '' . esc_html( $cache ) . '' 1366 | ); 1367 | } elseif ( 'user' === $type ) { 1368 | $messages[] = sprintf( 1369 | _n( 'Cleared %s key for user ID: %s', 'Cleared %s keys for user ID: %s', $keys, 'wp-spider-cache' ), 1370 | '' . esc_html( $keys ) . '', 1371 | '' . esc_html( $cache ) . '' 1372 | ); 1373 | } 1374 | } 1375 | 1376 | // Only show these when not using a version control system 1377 | if ( false === $this->vcs ) { 1378 | 1379 | // No object cache 1380 | if ( empty( $this->drop_ins['object'] ) ) { 1381 | 1382 | // Assemble the URL 1383 | $url = add_query_arg( array( 1384 | 'copy' => 'object', 1385 | 'nonce' => wp_create_nonce( self::FILE_NONCE ) 1386 | ), menu_page_url( 'wp-spider-cache', false ) ); 1387 | 1388 | $messages[] = sprintf( esc_html__( 'Persistent object caching is not enabled. %s', 'wp-spider-cache' ), '' . esc_html__( 'Enable?', 'wp-spider-cache' ) . '' ); 1389 | 1390 | // Using drop-in but engine 1391 | } elseif ( function_exists( 'wp_object_cache' ) ) { 1392 | 1393 | // Get cache engine 1394 | $cache_engine = wp_object_cache()->engine_class_name; 1395 | 1396 | // Missing cache engine extension 1397 | if ( empty( $cache_engine ) ) { 1398 | $messages[] = esc_html__( 'Cache engine name missing from Object Cache class.', 'wp-spider-cache' ); 1399 | } elseif ( ! extension_loaded( $cache_engine ) ) { 1400 | $messages[] = sprintf( esc_html__( 'Please install the %s extension.', 'wp-spider-cache' ), $cache_engine ); 1401 | } 1402 | 1403 | // No output cache 1404 | if ( empty( $this->drop_ins['output'] ) ) { 1405 | 1406 | // Assemble the URL 1407 | $url = add_query_arg( array( 1408 | 'copy' => 'output', 1409 | 'nonce' => wp_create_nonce( self::FILE_NONCE ) 1410 | ), menu_page_url( 'wp-spider-cache', false ) ); 1411 | 1412 | $messages[] = sprintf( esc_html__( 'Page caching is not enabled. %s', 'wp-spider-cache' ), '' . esc_html__( 'Enable?', 'wp-spider-cache' ) . '' ); 1413 | 1414 | // Using drop-in but WP_CACHE not set 1415 | } elseif ( ! defined( 'WP_CACHE' ) || ( false === WP_CACHE ) ) { 1416 | $messages[] = sprintf( esc_html__( 'Please add %s to your %s to enable page caching.', 'wp-spider-cache' ), "define( 'WP_CACHE', true );", 'wp-config.php' ); 1417 | } 1418 | } 1419 | } 1420 | 1421 | // Bail if no message 1422 | if ( empty( $messages ) ) { 1423 | return; 1424 | } ?> 1425 | 1426 |
1427 |

', $messages ); // May contain HTML ?>

1428 |
1429 | 1430 | group ); 1493 | 1494 | $retval = wp_cache_incr( "{$url_key}_version", 1, $output_cache->group ); 1495 | 1496 | $output_cache_no_remote_group_key = array_search( $output_cache->group, (array) $object_cache->no_remote_groups ); 1497 | 1498 | // The *_version key needs to be replicated remotely, otherwise invalidation won't work. 1499 | // The race condition here should be acceptable. 1500 | if ( false !== $output_cache_no_remote_group_key ) { 1501 | unset( $object_cache->no_remote_groups[ $output_cache_no_remote_group_key ] ); 1502 | $retval = wp_cache_set( "{$url_key}_version", $retval, $output_cache->group ); 1503 | $object_cache->no_remote_groups[ $output_cache_no_remote_group_key ] = $output_cache->group; 1504 | } 1505 | 1506 | return $retval; 1507 | } 1508 | } 1509 | 1510 | // Go web. Fly. Up, up, and away web! Shazam! Go! Go! Go web go! Tally ho! 1511 | add_action( 'plugins_loaded', array( 'WP_Spider_Cache_UI', 'init' ) ); 1512 | -------------------------------------------------------------------------------- /wp-spider-cache/assets/css/spider-cache.css: -------------------------------------------------------------------------------- 1 | @media screen and ( min-width: 782px ) { 2 | .spider-cache table th.blog-id, 3 | .spider-cache table th.count { 4 | width: 10%; 5 | } 6 | 7 | .spider-cache table th.cache-group { 8 | width: 12%; 9 | } 10 | 11 | .spider-cache table th.keys { 12 | width: 60%; 13 | } 14 | } 15 | 16 | @media screen and ( max-width: 782px ) { 17 | div#TB_window.thickbox-loading { 18 | width: 100% !important; 19 | height: 100% !important; 20 | margin-left: 0 !important; 21 | margin-top: 0 !important; 22 | top: 0 !important; 23 | left: 0 !important; 24 | } 25 | 26 | div.spider-cache table.wp-list-table tr.sc-no-results td.column-no-results, 27 | div.spider-cache table.wp-list-table tr.sc-refresh-results td.column-refreshing { 28 | display: table-cell; 29 | } 30 | } 31 | 32 | .spider-cache .tablenav .alignright form { 33 | display: inline-block; 34 | } 35 | 36 | #TB_ajaxContent table.sc-item td, 37 | #TB_ajaxContent table.sc-item th { 38 | padding: 0; 39 | margin: 0; 40 | } 41 | 42 | #TB_ajaxContent table.sc-item th { 43 | vertical-align: middle; 44 | width: 20%; 45 | } 46 | 47 | #TB_ajaxContent table.sc-item pre { 48 | font-family: monospace; 49 | display: inline-block; 50 | padding: 5px 10px; 51 | margin: 3px; 52 | background-color: #eee; 53 | line-height: 2em; 54 | overflow: auto; 55 | white-space: pre; 56 | tab-size: 4; 57 | } 58 | 59 | #TB_ajaxContent table.sc-item pre span { 60 | display: inline; 61 | } 62 | 63 | .spider-cache .sc-toolbar-secondary { 64 | float: left; 65 | height: 100%; 66 | margin: 12px 5px; 67 | } 68 | 69 | .spider-cache .sc-toolbar-primary { 70 | max-width: 33%; 71 | height: 100%; 72 | } 73 | 74 | .spider-cache .item .row-actions { 75 | display: inline-block; 76 | } 77 | 78 | .spider-cache .row-updating .check-column { 79 | background: url('../img/spinner.gif') 10px 9px no-repeat; 80 | } 81 | @media print, 82 | (-o-min-device-pixel-ratio: 5/4), 83 | (-webkit-min-device-pixel-ratio: 1.25), 84 | (min-resolution: 120dpi) { 85 | .spider-cache .row-updating .check-column { 86 | background-image: url('../img/spinner-2x.gif'); 87 | background-size: 20px 20px; 88 | } 89 | } 90 | 91 | .spider-cache .row-updating .check-column input { 92 | visibility: hidden; 93 | } 94 | -------------------------------------------------------------------------------- /wp-spider-cache/assets/img/spinner-2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuttter/wp-spider-cache/8bc048cc7d11f7a79c9b404c7eb7b2ee84a24d03/wp-spider-cache/assets/img/spinner-2x.gif -------------------------------------------------------------------------------- /wp-spider-cache/assets/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stuttter/wp-spider-cache/8bc048cc7d11f7a79c9b404c7eb7b2ee84a24d03/wp-spider-cache/assets/img/spinner.gif -------------------------------------------------------------------------------- /wp-spider-cache/assets/js/spider-cache.js: -------------------------------------------------------------------------------- 1 | /* global window, document, $, jQuery, ajaxurl, WP_Spider_Cache */ 2 | 3 | ( function ( $ ) { 4 | "use strict"; 5 | 6 | var $instanceStore, 7 | $refreshInstance, 8 | $instanceSelector, 9 | $adminType, 10 | $showItem, 11 | $searchResults, 12 | $modalWindow; 13 | 14 | /** 15 | * 16 | * @param {data} e 17 | * @param {element} elem 18 | * @returns {void} 19 | */ 20 | function maybe_remove_group( e, elem ) { 21 | var result = $.parseJSON( e ), 22 | row = elem.parents( 'tr' ), 23 | key = row; 24 | 25 | setTimeout( function() { 26 | if ( result.success ) { 27 | key.fadeOut( 500, function() { 28 | key.remove(); 29 | } ); 30 | } 31 | 32 | row.removeClass( 'row-updating' ); 33 | }, 500 ); 34 | } 35 | 36 | /** 37 | * Maybe remove a cache key item from the list-table 38 | * 39 | * @param {data} e 40 | * @param {element} elem 41 | * @returns {void} 42 | */ 43 | function maybe_remove_item( e, elem ) { 44 | var result = $.parseJSON( e ), 45 | row = elem.parents( 'tr' ), 46 | col = elem.parents( 'td' ); 47 | 48 | if ( 1 === col.children( 'div.item' ).length ) { 49 | var key = row; 50 | } else { 51 | var key = elem.parents( 'div.item' ); 52 | } 53 | 54 | setTimeout( function() { 55 | if ( result.success ) { 56 | key.fadeOut( 500, function() { 57 | key.remove(); 58 | } ); 59 | } 60 | 61 | row.removeClass( 'row-updating' ); 62 | }, 500 ); 63 | } 64 | 65 | function handleChange( e ) { 66 | var el = $( e.currentTarget ), 67 | val = $.trim( el.val() ); 68 | 69 | if ( val ) { 70 | $refreshInstance.prop( 'disabled', true ); 71 | $instanceStore.html( WP_Spider_Cache.refreshing_results ); 72 | $.ajax( { 73 | type : 'post', 74 | url : ajaxurl, 75 | data : { 76 | action : 'sc-get-instance', 77 | nonce : el.data( 'nonce' ), 78 | name : val, 79 | type : $adminType.val() 80 | }, 81 | cache : false, 82 | success : function ( data ) { 83 | setTimeout( function() { 84 | if ( data ) { 85 | $instanceStore.html( data ); 86 | searchTable( $searchResults ); 87 | } else { 88 | $instanceStore.html( WP_Spider_Cache.no_results ); 89 | } 90 | $refreshInstance.prop( 'disabled', false ); 91 | }, 500 ); 92 | }, 93 | error : function () { 94 | setTimeout( function() { 95 | $instanceStore.html( WP_Spider_Cache.no_results ); 96 | $refreshInstance.prop( 'disabled', false ); 97 | }, 500 ); 98 | } 99 | } ); 100 | } 101 | } 102 | 103 | function searchTable( element ) { 104 | var value = $( element ).val(); 105 | 106 | $instanceStore.children( 'tr' ).each( function() { 107 | if ( $( this ).text().indexOf( value ) > -1 ) { 108 | $( this ).show(); 109 | } else { 110 | $( this ).hide(); 111 | } 112 | } ); 113 | } 114 | 115 | $( document ).ready( function () { 116 | $instanceStore = $( '.sc-contents' ); 117 | $refreshInstance = $( '.sc-refresh-instance' ); 118 | $instanceSelector = $( '.sc-server-selector' ); 119 | $adminType = $( '#sc-admin-type' ); 120 | $showItem = $( '#sc-show-item' ); 121 | $searchResults = $( '#sc-search-input' ); 122 | 123 | $searchResults.keyup( function() { 124 | searchTable( this ); 125 | } ); 126 | 127 | $refreshInstance.click( function () { 128 | $instanceSelector.trigger( 'change' ); 129 | return false; 130 | } ); 131 | 132 | $instanceSelector.bind( 'change', handleChange ); 133 | 134 | $( document.body ) 135 | .on( 'click', '.sc-flush-group', function ( e ) { 136 | var elem = $( e.currentTarget ), 137 | keys = []; 138 | 139 | elem.parents( 'td' ).next().find( 'div.item' ).each( function () { 140 | keys.push( $( this ).data( 'key' ) ); 141 | } ); 142 | 143 | elem.parents( 'tr' ).addClass( 'row-updating' ); 144 | 145 | $.ajax( { 146 | type : 'post', 147 | url : e.currentTarget.href, 148 | data : { 149 | keys : keys 150 | }, 151 | success : function ( e ) { 152 | maybe_remove_group( e, elem ); 153 | } 154 | } ); 155 | 156 | return false; 157 | } ) 158 | .on( 'click', '.sc-remove-item', function ( e ) { 159 | var elem = $( e.currentTarget ); 160 | 161 | elem.parents( 'tr' ).addClass( 'row-updating' ); 162 | 163 | $.ajax( { 164 | type : 'post', 165 | url : e.currentTarget.href, 166 | success : function ( e ) { 167 | maybe_remove_item( e, elem ); 168 | } 169 | } ); 170 | 171 | return false; 172 | } ) 173 | .on( 'click', '.sc-view-item', function ( e ) { 174 | var elem = $( e.currentTarget ); 175 | 176 | elem.parents( 'tr' ).addClass( 'row-updating' ); 177 | 178 | $.ajax( { 179 | type : 'post', 180 | url : e.currentTarget.href, 181 | success : function ( data ) { 182 | $modalWindow = $( '#TB_ajaxContent' ); 183 | $showItem.html( data ).detach().appendTo( $modalWindow ); 184 | elem.parents( 'tr' ).removeClass( 'row-updating' ); 185 | } 186 | } ); 187 | 188 | return false; 189 | } ); 190 | 191 | } ); 192 | }( jQuery ) ); 193 | -------------------------------------------------------------------------------- /wp-spider-cache/drop-ins/advanced-cache.php: -------------------------------------------------------------------------------- 1 | daemon_class_name ) ) { 70 | $class_name = $this->daemon_class_name; 71 | $this->success_code = $class_name::RES_SUCCESS; 72 | $this->preserve_order = $class_name::GET_PRESERVE_ORDER; 73 | } 74 | } 75 | } 76 | endif; 77 | -------------------------------------------------------------------------------- /wp-spider-cache/includes/class-object-memory.php: -------------------------------------------------------------------------------- 1 | {$name}; 101 | } 102 | 103 | /** 104 | * Makes private properties settable for backward compatibility. 105 | * 106 | * @since 7.0.0 107 | * 108 | * @param string $name Property to set. 109 | * @param mixed $value Property value. 110 | * @return mixed Newly-set property. 111 | */ 112 | public function __set( $name, $value ) { 113 | return $this->{$name} = $value; 114 | } 115 | 116 | /** 117 | * Makes private properties checkable for backward compatibility. 118 | * 119 | * @since 7.0.0 120 | * 121 | * @param string $name Property to check if set. 122 | * @return bool Whether the property is set. 123 | */ 124 | public function __isset( $name ) { 125 | return isset( $this->{$name} ); 126 | } 127 | 128 | /** 129 | * Makes private properties un-settable for backward compatibility. 130 | * 131 | * @since 7.0.0 132 | * 133 | * @param string $name Property to unset. 134 | */ 135 | public function __unset( $name ) { 136 | unset( $this->{$name} ); 137 | } 138 | 139 | /** 140 | * Adds data to the cache if it doesn't already exist. 141 | * 142 | * @since 7.0.0 143 | * 144 | * @uses WP_Object_Cache::_exists() Checks to see if the cache already has data. 145 | * @uses WP_Object_Cache::set() Sets the data after the checking the cache 146 | * contents existence. 147 | * 148 | * @param int|string $key What to call the contents in the cache. 149 | * @param mixed $value The contents to store in the cache. 150 | * @param string $group Optional. Where to group the cache contents. Default 'default'. 151 | * @param int $expire Optional. When to expire the cache contents. Default 0 (no expiration). 152 | * @return bool False if cache key and group already exist, true on success 153 | */ 154 | public function add( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) { 155 | 156 | /** 157 | * Ensuring that wp_suspend_cache_addition is defined before calling, 158 | * because sometimes an advanced-cache.php file will load object-cache.php 159 | * before wp-includes/functions.php is loaded. 160 | * 161 | * In those cases, if wp_cache_add() is called in advanced-cache.php 162 | * before any more of WordPress is loaded, we get a fatal error because 163 | * wp_suspend_cache_addition() will not be defined until 164 | * wp-includes/functions.php is loaded. 165 | */ 166 | if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) { 167 | return false; 168 | } 169 | 170 | if ( empty( $group ) ) { 171 | $group = 'default'; 172 | } 173 | 174 | $id = $key; 175 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 176 | $id = $this->blog_prefix . $key; 177 | } 178 | 179 | if ( $this->_exists( $id, $group ) ) { 180 | return false; 181 | } 182 | 183 | return $this->set( $key, $value, $group, (int) $expiration ); 184 | } 185 | 186 | /** 187 | * Sets the list of global cache groups. 188 | * 189 | * @since 7.0.0 190 | * 191 | * @param array $groups List of groups that are global. 192 | */ 193 | public function add_global_groups( $groups = array() ) { 194 | $groups = array_fill_keys( (array) $groups, true ); 195 | $this->global_groups = array_merge( $this->global_groups, $groups ); 196 | } 197 | 198 | /** 199 | * Decrements numeric cache item's value. 200 | * 201 | * @since 7.0.0 202 | * 203 | * @param int|string $key The cache key to decrement. 204 | * @param int $offset Optional. The amount by which to decrement the item's value. Default 1. 205 | * @param string $group Optional. The group the key is in. Default 'default'. 206 | * @return false|int False on failure, the item's new value on success. 207 | */ 208 | public function decr( $key, $offset = 1, $group = 'default' ) { 209 | if ( empty( $group ) ) { 210 | $group = 'default'; 211 | } 212 | 213 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 214 | $key = $this->blog_prefix . $key; 215 | } 216 | 217 | if ( ! $this->_exists( $key, $group ) ) { 218 | return false; 219 | } 220 | 221 | if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) { 222 | $this->cache[ $group ][ $key ] = 0; 223 | } 224 | 225 | $offset = (int) $offset; 226 | 227 | $this->cache[ $group ][ $key ] -= $offset; 228 | 229 | if ( $this->cache[ $group ][ $key ] < 0 ) { 230 | $this->cache[ $group ][ $key ] = 0; 231 | } 232 | 233 | return $this->cache[ $group ][ $key ]; 234 | } 235 | 236 | /** 237 | * Removes the contents of the cache key in the group. 238 | * 239 | * If the cache key does not exist in the group, then nothing will happen. 240 | * 241 | * @since 7.0.0 242 | * 243 | * @param int|string $key What the contents in the cache are called. 244 | * @param string $group Optional. Where the cache contents are grouped. Default 'default'. 245 | * @param bool $deprecated Optional. Unused. Default false. 246 | * @return bool False if the contents weren't deleted and true on success. 247 | */ 248 | public function delete( $key, $group = 'default', $time = 0, $server_key = '', $byKey = false ) { 249 | if ( empty( $group ) ) { 250 | $group = 'default'; 251 | } 252 | 253 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 254 | $key = $this->blog_prefix . $key; 255 | } 256 | 257 | if ( ! $this->_exists( $key, $group ) ) { 258 | return false; 259 | } 260 | 261 | unset( $this->cache[$group][$key] ); 262 | return true; 263 | } 264 | 265 | /** 266 | * Clears the object cache of all data. 267 | * 268 | * @since 7.0.0 269 | * 270 | * @return true Always returns true. 271 | */ 272 | public function flush( $delay = 0 ) { 273 | $this->cache = array(); 274 | 275 | return true; 276 | } 277 | 278 | /** 279 | * Retrieves the cache contents, if it exists. 280 | * 281 | * The contents will be first attempted to be retrieved by searching by the 282 | * key in the cache group. If the cache is hit (success) then the contents 283 | * are returned. 284 | * 285 | * On failure, the number of cache misses will be incremented. 286 | * 287 | * @since 7.0.0 288 | * 289 | * @param int|string $key What the contents in the cache are called. 290 | * @param string $group Optional. Where the cache contents are grouped. Default 'default'. 291 | * @param string $force Optional. Unused. Whether to force a refetch rather than relying on the local 292 | * cache. Default false. 293 | * @param bool $found Optional. Whether the key was found in the cache (passed by reference). 294 | * Disambiguates a return of false, a storable value. Default null. 295 | * @return false|mixed False on failure to retrieve contents or the cache contents on success. 296 | */ 297 | public function get( $key, $group = 'default', $force = false, &$found = NULL, $server_key = '', $byKey = false, $cache_cb = NULL, &$cas_token = NULL ) { 298 | if ( empty( $group ) ) { 299 | $group = 'default'; 300 | } 301 | 302 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 303 | $key = $this->blog_prefix . $key; 304 | } 305 | 306 | if ( $this->_exists( $key, $group ) ) { 307 | $found = true; 308 | $this->cache_hits += 1; 309 | if ( is_object($this->cache[$group][$key]) ) { 310 | return clone $this->cache[$group][$key]; 311 | } else { 312 | return $this->cache[$group][$key]; 313 | } 314 | } 315 | 316 | $found = false; 317 | $this->cache_misses += 1; 318 | return false; 319 | } 320 | 321 | /** 322 | * Increments numeric cache item's value. 323 | * 324 | * @since 7.0.0 325 | * 326 | * @param int|string $key The cache key to increment 327 | * @param int $offset Optional. The amount by which to increment the item's value. Default 1. 328 | * @param string $group Optional. The group the key is in. Default 'default'. 329 | * @return false|int False on failure, the item's new value on success. 330 | */ 331 | public function incr( $key, $offset = 1, $group = 'default' ) { 332 | if ( empty( $group ) ) { 333 | $group = 'default'; 334 | } 335 | 336 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 337 | $key = $this->blog_prefix . $key; 338 | } 339 | 340 | if ( ! $this->_exists( $key, $group ) ) { 341 | return false; 342 | } 343 | 344 | if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) { 345 | $this->cache[ $group ][ $key ] = 0; 346 | } 347 | 348 | $offset = (int) $offset; 349 | 350 | $this->cache[ $group ][ $key ] += $offset; 351 | 352 | if ( $this->cache[ $group ][ $key ] < 0 ) { 353 | $this->cache[ $group ][ $key ] = 0; 354 | } 355 | 356 | return $this->cache[ $group ][ $key ]; 357 | } 358 | 359 | /** 360 | * Replaces the contents in the cache, if contents already exist. 361 | * 362 | * @since 7.0.0 363 | * 364 | * @see WP_Object_Cache::set() 365 | * 366 | * @param int|string $key What to call the contents in the cache. 367 | * @param mixed $value The contents to store in the cache. 368 | * @param string $group Optional. Where to group the cache contents. Default 'default'. 369 | * @param int $expire Optional. When to expire the cache contents. Default 0 (no expiration). 370 | * @return bool False if not exists, true if contents were replaced. 371 | */ 372 | public function replace( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) { 373 | if ( empty( $group ) ) { 374 | $group = 'default'; 375 | } 376 | 377 | $id = $key; 378 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 379 | $id = $this->blog_prefix . $key; 380 | } 381 | 382 | if ( ! $this->_exists( $id, $group ) ) { 383 | return false; 384 | } 385 | 386 | return $this->set( $key, $value, $group, (int) $expiration ); 387 | } 388 | 389 | /** 390 | * Resets cache keys. 391 | * 392 | * @since 7.0.0 393 | * 394 | * @deprecated 3.5.0 Use switch_to_blog() 395 | * @see switch_to_blog() 396 | */ 397 | public function reset() { 398 | _deprecated_function( __FUNCTION__, '3.5.0', 'switch_to_blog()' ); 399 | 400 | // Clear out non-global caches since the blog ID has changed. 401 | foreach ( array_keys( $this->cache ) as $group ) { 402 | if ( ! isset( $this->global_groups[ $group ] ) ) { 403 | unset( $this->cache[ $group ] ); 404 | } 405 | } 406 | } 407 | 408 | /** 409 | * Sets the data contents into the cache. 410 | * 411 | * The cache contents is grouped by the $group parameter followed by the 412 | * $key. This allows for duplicate ids in unique groups. Therefore, naming of 413 | * the group should be used with care and should follow normal function 414 | * naming guidelines outside of core WordPress usage. 415 | * 416 | * The $expire parameter is not used, because the cache will automatically 417 | * expire for each time a page is accessed and PHP finishes. The method is 418 | * more for cache plugins which use files. 419 | * 420 | * @since 7.0.0 421 | * 422 | * @param int|string $key What to call the contents in the cache. 423 | * @param mixed $value The contents to store in the cache. 424 | * @param string $group Optional. Where to group the cache contents. Default 'default'. 425 | * @param int $expire Not Used. 426 | * @return true Always returns true. 427 | */ 428 | public function set( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) { 429 | if ( empty( $group ) ) { 430 | $group = 'default'; 431 | } 432 | 433 | if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) { 434 | $key = $this->blog_prefix . $key; 435 | } 436 | 437 | if ( is_object( $value ) ) { 438 | $value = clone $value; 439 | } 440 | 441 | $this->cache[$group][$key] = $value; 442 | return true; 443 | } 444 | 445 | /** 446 | * Echoes the stats of the caching. 447 | * 448 | * Gives the cache hits, and cache misses. Also prints every cached group, 449 | * key and the data. 450 | * 451 | * @since 7.0.0 452 | */ 453 | public function stats() { 454 | echo "

"; 455 | echo "Cache Hits: {$this->cache_hits}
"; 456 | echo "Cache Misses: {$this->cache_misses}
"; 457 | echo "

"; 458 | echo ''; 463 | } 464 | 465 | /** 466 | * Switches the internal blog ID. 467 | * 468 | * This changes the blog ID used to create keys in blog specific groups. 469 | * 470 | * @since 7.0.0 471 | * 472 | * @param int $blog_id Blog ID. 473 | */ 474 | public function switch_to_blog( $blog_id = 0 ) { 475 | $blog_id = (int) $blog_id; 476 | $this->blog_prefix = $this->multisite 477 | ? $blog_id . ':' 478 | : ''; 479 | } 480 | 481 | /** 482 | * Serves as a utility function to determine whether a key exists in the cache. 483 | * 484 | * @since 7.0.0 485 | * 486 | * @param int|string $key Cache key to check for existence. 487 | * @param string $group Cache group for the key existence check. 488 | * @return bool Whether the key exists in the cache for the given group. 489 | */ 490 | protected function _exists( $key, $group ) { 491 | return isset( $this->cache[ $group ] ) && ( isset( $this->cache[ $group ][ $key ] ) || array_key_exists( $key, $this->cache[ $group ] ) ); 492 | } 493 | 494 | /** 495 | * Sets up object properties; PHP 5 style constructor. 496 | * 497 | * @since 7.0.0 498 | */ 499 | public function __construct() { 500 | $this->multisite = is_multisite(); 501 | $this->switch_to_blog( $GLOBALS['blog_id'] ); 502 | 503 | /** 504 | * @todo This should be moved to the PHP4 style constructor, PHP5 505 | * already calls __destruct() 506 | */ 507 | register_shutdown_function( array( $this, '__destruct' ) ); 508 | } 509 | 510 | /** 511 | * Saves the object cache before object is completely destroyed. 512 | * 513 | * Called upon object destruction, which should be when PHP ends. 514 | * 515 | * @since 7.0.0 516 | * 517 | * @return true Always returns true. 518 | */ 519 | public function __destruct() { 520 | return true; 521 | } 522 | } 523 | endif; -------------------------------------------------------------------------------- /wp-spider-cache/includes/class-object-redis.php: -------------------------------------------------------------------------------- 1 | value or name=>array(values). 77 | * These will be sent with every response from the cache. 78 | * 79 | * @var array 80 | */ 81 | public $headers = array(); 82 | 83 | /** 84 | * Set true to enable redirect caching. 85 | * 86 | * @var bool 87 | */ 88 | public $cache_redirects = false; 89 | 90 | /** 91 | * This is set to the response code during a redirect. 92 | * 93 | * @var bool 94 | */ 95 | public $redirect_status = false; 96 | 97 | /** 98 | * This is set to the redirect location. 99 | * 100 | * @var bool 101 | */ 102 | public $redirect_location = false; 103 | 104 | /** 105 | * These headers will never be cached. Apply strtolower. 106 | * 107 | * @var array 108 | */ 109 | public $uncached_headers = array( 'transfer-encoding' ); 110 | 111 | /** 112 | * Set false to hide the spider_cache info 113 | * 114 | * @var bool 115 | */ 116 | public $debug = false; 117 | 118 | /** 119 | * Set false to disable Last-Modified and Cache-Control headers 120 | * 121 | * @var bool 122 | */ 123 | public $cache_control = true; 124 | 125 | /** 126 | * Change this to cancel the output buffer. Use wp_output_cache_cancel(); 127 | * 128 | * @var bool 129 | */ 130 | public $cancel = false; 131 | 132 | /** 133 | * Names of cookies - if they exist and the cache would normally be 134 | * bypassed, don't bypass it. 135 | * 136 | * @var array 137 | */ 138 | public $noskip_cookies = array(); 139 | 140 | /** 141 | * Names of cookies - if they exist bypass caching. 142 | * 143 | * @var array 144 | */ 145 | public $skip_cookies = array(); 146 | 147 | /** 148 | * Used internally 149 | * 150 | * @var bool 151 | */ 152 | private $genlock = false; 153 | 154 | /** 155 | * Used internally 156 | * 157 | * @var bool 158 | */ 159 | private $do = false; 160 | 161 | /** 162 | * The request count 163 | * 164 | * @var int 165 | */ 166 | private $requests = 0; 167 | 168 | /** 169 | * The permalink 170 | * 171 | * @var string 172 | */ 173 | private $permalink = ''; 174 | 175 | /** 176 | * The URL key 177 | * 178 | * @var string 179 | */ 180 | private $url_key = ''; 181 | 182 | /** 183 | * The URL version 184 | * 185 | * @var string 186 | */ 187 | private $url_version = ''; 188 | 189 | /** 190 | * The status header 191 | * 192 | * @var string 193 | */ 194 | private $status_header = 'HTTP/1.1 200 OK'; 195 | 196 | /** 197 | * The status code 198 | * 199 | * @var int 200 | */ 201 | private $status_code = 200; 202 | 203 | /** 204 | * The cache data 205 | * 206 | * @var array 207 | */ 208 | private $cache = array(); 209 | 210 | /** 211 | * The key data 212 | * 213 | * @var array 214 | */ 215 | private $keys = array(); 216 | 217 | /** 218 | * The key 219 | * 220 | * @var string 221 | */ 222 | private $key = ''; 223 | 224 | /** 225 | * The request cache key 226 | * 227 | * @var string 228 | */ 229 | private $req_key = ''; 230 | 231 | /** 232 | * Setup the query attribute if a query string exists 233 | * 234 | * @since 6.0.0 235 | */ 236 | private $query = array(); 237 | 238 | /** 239 | * Main output cache constructor 240 | * 241 | * @since 2.0.0 242 | */ 243 | public function __construct() { 244 | 245 | // Maybe pull values from pre-existing output cache global 246 | if ( ! empty( $GLOBALS['wp_output_cache'] ) && is_array( $GLOBALS['wp_output_cache'] ) ) { 247 | foreach ( $GLOBALS['wp_output_cache'] as $key => $value ) { 248 | $this->{$key} = $value; 249 | } 250 | } 251 | 252 | // Bail if cookies are present 253 | if ( $this->has_cookies() ) { 254 | return; 255 | } 256 | 257 | // Support cache debug constant 258 | if ( defined( 'WP_CACHE_DEBUG' ) ) { 259 | $this->debug = (bool) WP_CACHE_DEBUG; 260 | } 261 | 262 | // Set the started time 263 | $this->started = time(); 264 | 265 | // Always set cache groups 266 | $this->configure_groups(); 267 | 268 | // Start caching 269 | $this->start(); 270 | } 271 | 272 | /** 273 | * Set the status header & code 274 | * 275 | * @since 2.0.0 276 | * 277 | * @param string $status_header 278 | * @param int $status_code 279 | * 280 | * @return string 281 | */ 282 | public function status_header( $status_header, $status_code = 200 ) { 283 | $this->status_header = $status_header; 284 | $this->status_code = (int) $status_code; 285 | 286 | return $status_header; 287 | } 288 | 289 | /** 290 | * Set the redirect status, and whether it should be cached 291 | * 292 | * @since 2.0.0 293 | * 294 | * @param string $status 295 | * @param string $location 296 | * 297 | * @return string 298 | */ 299 | public function redirect_status( $status, $location ) { 300 | 301 | // Cache this redirect 302 | if ( true === $this->cache_redirects ) { 303 | $this->redirect_status = $status; 304 | $this->redirect_location = $location; 305 | } 306 | 307 | return $status; 308 | } 309 | 310 | /** 311 | * Merge the headers and send them off 312 | * 313 | * @since 2.0.0 314 | * 315 | * @param array $headers1 316 | * @param array $headers2 317 | */ 318 | protected function do_headers( $headers1, $headers2 = array() ) { 319 | 320 | // Merge the arrays of headers into one 321 | $headers = array(); 322 | $keys = array_unique( array_merge( array_keys( $headers1 ), array_keys( $headers2 ) ) ); 323 | 324 | foreach ( $keys as $k ) { 325 | $headers[ $k ] = array(); 326 | 327 | if ( isset( $headers1[ $k ] ) && isset( $headers2[ $k ] ) ) { 328 | $headers[ $k ] = array_merge( (array) $headers2[ $k ], (array) $headers1[ $k ] ); 329 | } elseif ( isset( $headers2[ $k ] ) ) { 330 | $headers[ $k ] = (array) $headers2[ $k ]; 331 | } else { 332 | $headers[ $k ] = (array) $headers1[ $k ]; 333 | } 334 | 335 | $headers[ $k ] = array_unique( $headers[ $k ] ); 336 | } 337 | 338 | // These headers take precedence over any previously sent with the same names 339 | foreach ( $headers as $k => $values ) { 340 | $clobber = true; 341 | foreach ( $values as $v ) { 342 | @header( "{$k}: {$v}", $clobber ); 343 | $clobber = false; 344 | } 345 | } 346 | } 347 | 348 | /** 349 | * Configure the cache client 350 | * 351 | * @since 2.0.0 352 | */ 353 | protected function configure_groups() { 354 | 355 | // Local cache instance only 356 | if ( ( false === $this->remote ) && function_exists( 'wp_cache_add_no_remote_groups' ) ) { 357 | wp_cache_add_no_remote_groups( array( $this->group ) ); 358 | } 359 | } 360 | 361 | /** 362 | * Start the output buffer 363 | * 364 | * @since 2.0.0 365 | * 366 | * @param string $output 367 | * @return string 368 | */ 369 | protected function ob( $output = '' ) { 370 | 371 | // Bail if cancelling 372 | if ( true === $this->cancel ) { 373 | return $output; 374 | } 375 | 376 | // PHP5 and objects disappearing before output buffers? 377 | wp_object_cache_init(); 378 | 379 | // $wp_object_cache was clobbered in wp-settings.php so repeat this 380 | $this->configure_groups(); 381 | 382 | // Unlock regeneration 383 | wp_cache_delete( "{$this->url_key}_genlock", $this->group ); 384 | 385 | // Do not cache blank pages unless they are HTTP redirects 386 | $output = trim( $output ); 387 | if ( empty( $output ) && ( empty( $this->redirect_status ) || empty( $this->redirect_location ) ) ) { 388 | return $output; 389 | } 390 | 391 | // Do not cache 5xx responses 392 | if ( isset( $this->status_code ) && intval( $this->status_code / 100 ) === 5 ) { 393 | return $output; 394 | } 395 | 396 | // Variants and keys 397 | $this->do_variants( $this->vary ); 398 | $this->generate_keys(); 399 | 400 | // Construct and save the spider_cache 401 | $this->cache = array( 402 | 'output' => $output, 403 | 'time' => $this->started, 404 | 'headers' => array(), 405 | 'timer' => $this->timer_stop( false, 3 ), 406 | 'status_header' => $this->status_header, 407 | 'redirect_status' => $this->redirect_status, 408 | 'redirect_location' => $this->redirect_location, 409 | 'version' => $this->url_version 410 | ); 411 | 412 | // PHP5 and higher ( 413 | foreach ( headers_list() as $header ) { 414 | list( $k, $v ) = array_map( 'trim', explode( ':', $header, 2 ) ); 415 | $this->cache['headers'][ $k ] = array( $v ); 416 | } 417 | 418 | // Unset uncached headers 419 | if ( ! empty( $this->cache['headers'] ) && ! empty( $this->uncached_headers ) ) { 420 | foreach ( $this->uncached_headers as $header ) { 421 | unset( $this->cache['headers'][ $header ] ); 422 | } 423 | } 424 | 425 | // Set cached headers 426 | foreach ( $this->cache['headers'] as $header => $values ) { 427 | 428 | // Bail if cookies were set 429 | if ( strtolower( $header ) === 'set-cookie' ) { 430 | return $output; 431 | } 432 | 433 | foreach ( (array) $values as $value ) { 434 | if ( preg_match( '/^Cache-Control:.*max-?age=(\d+)/i', "{$header}: {$value}", $matches ) ) { 435 | $this->max_age = intval( $matches[1] ); 436 | } 437 | } 438 | } 439 | 440 | // Set max-age & expiration 441 | $this->cache['max_age'] = $this->max_age; 442 | $this->cache['expires'] = $this->max_age + $this->seconds + 30; 443 | 444 | // Set cache 445 | wp_cache_set( $this->key, $this->cache, $this->group, $this->cache['expires'] ); 446 | 447 | // Cache control 448 | if ( true === $this->cache_control ) { 449 | 450 | // Don't clobber Last-Modified header if already set, e.g. by WP::send_headers() 451 | if ( ! isset( $this->cache['headers']['Last-Modified'] ) ) { 452 | @header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this->cache['time'] ) . ' GMT', true ); 453 | } 454 | 455 | if ( ! isset( $this->cache['headers']['Cache-Control'] ) ) { 456 | @header( "Cache-Control: max-age={$this->max_age}, must-revalidate", false ); 457 | } 458 | } 459 | 460 | $this->do_headers( $this->headers ); 461 | 462 | // Add some debug info just before debug ) { 464 | $this->add_debug_just_cached(); 465 | } 466 | 467 | // Pass output to next ob handler 468 | return $this->cache['output']; 469 | } 470 | 471 | /** 472 | * Add a variant to the cache key 473 | * 474 | * @since 2.0.0 475 | * 476 | * @param string $function 477 | */ 478 | public function add_variant( $function = '' ) { 479 | $key = md5( $function ); 480 | $this->vary[ $key ] = $function; 481 | } 482 | 483 | /** 484 | * Set the cache with it's variant keys 485 | * 486 | * @since 2.0.0 487 | * 488 | * @param mixed $dimensions False by default, array of arguments otherwise 489 | */ 490 | protected function do_variants( $dimensions = false ) { 491 | 492 | // This function is called without arguments early in the page load, 493 | // then with arguments during the OB handler. 494 | if ( false === $dimensions ) { 495 | $dimensions = wp_cache_get( "{$this->url_key}_vary", $this->group ); 496 | } elseif ( ! empty( $dimensions ) ) { 497 | wp_cache_set( "{$this->url_key}_vary", $dimensions, $this->group, $this->max_age + 10 ); 498 | } 499 | 500 | // Bail if no dimensions 501 | if ( empty( $dimensions ) || ! is_array( $dimensions ) ) { 502 | return; 503 | } 504 | 505 | // Sort keys 506 | ksort( $dimensions ); 507 | 508 | // Evaluate 509 | foreach ( $dimensions as $key => $function ) { 510 | 511 | // Callable (PHP7.2 and higher) 512 | if ( is_callable( $function ) ) { 513 | $this->keys[ $key ] = $function(); 514 | 515 | // Raw code (Deprecated in PHP8.0) 516 | } elseif ( function_exists( 'create_function' ) ) { 517 | $created_function = create_function( '', $function ); 518 | $this->keys[ $key ] = $created_function(); 519 | } 520 | } 521 | } 522 | 523 | /** 524 | * Generate cache keys for the request 525 | * 526 | * @since 2.0.0 527 | */ 528 | protected function generate_keys() { 529 | $this->key = md5( serialize( $this->keys ) ); 530 | $this->req_key = "{$this->key}_req"; 531 | } 532 | 533 | /** 534 | * Add some debug info 535 | * 536 | * @since 2.0.0 537 | */ 538 | protected function add_debug_just_cached() { 539 | $generation = $this->cache['timer']; 540 | $bytes = strlen( serialize( $this->cache ) ); 541 | $html = <<max_age} seconds 545 | --> 546 | 547 | HTML; 548 | $this->add_debug_html_to_output( $html ); 549 | } 550 | 551 | /** 552 | * Add verbose debug info 553 | * 554 | * @since 2.0.0 555 | */ 556 | protected function add_debug_from_cache() { 557 | $time = $this->started; 558 | $seconds_ago = $time - $this->cache['time']; 559 | $generation = $this->cache['timer']; 560 | $serving = $this->timer_stop( false, 3 ); 561 | $expires = $this->cache['max_age'] - $time + $this->cache['time']; 562 | $html = << 569 | 570 | HTML; 571 | $this->add_debug_html_to_output( $html ); 572 | } 573 | 574 | /** 575 | * Determine where to put what debug output 576 | * 577 | * @since 2.0.0 578 | * 579 | * @param string $debug_html 580 | * @return void 581 | */ 582 | protected function add_debug_html_to_output( $debug_html = '' ) { 583 | 584 | // Casing on the Content-Type header is inconsistent 585 | foreach ( array( 'Content-Type', 'Content-type' ) as $key ) { 586 | if ( isset( $this->cache['headers'][ $key ][0] ) && 0 !== strpos( $this->cache['headers'][ $key ][0], 'text/html' ) ) { 587 | return; 588 | } 589 | } 590 | 591 | // Bail if output does not include a head tag 592 | $head_position = strpos( $this->cache['output'], ' tag 598 | $this->cache['output'] = substr_replace( $this->cache['output'], $debug_html, $head_position, 0 ); 599 | } 600 | 601 | /** 602 | * Start page caching and hook into requests to complete it later 603 | * 604 | * @since 2.1.0 605 | * 606 | * @return void 607 | */ 608 | protected function start() { 609 | 610 | // Disabled 611 | if ( $this->max_age < 1 ) { 612 | return; 613 | } 614 | 615 | // Necessary to prevent clients using cached version after login cookies 616 | // set. If this is a problem, comment it out and remove all 617 | // Last-Modified headers. 618 | @header( 'Vary: Cookie', false ); 619 | 620 | // Setup attributes 621 | $this->setup_query(); 622 | $this->setup_protocol(); 623 | $this->setup_keys(); 624 | $this->setup_url(); 625 | 626 | // Setup keys and variants 627 | $this->do_variants(); 628 | $this->generate_keys(); 629 | 630 | // Maybe output from cache 631 | $this->maybe_update_cache(); 632 | $this->maybe_output_cache(); 633 | 634 | // Didn't meet the minimum condition? 635 | if ( ( false === $this->do ) && ( false === $this->genlock ) ) { 636 | return; 637 | } 638 | 639 | // Headers and such 640 | add_filter( 'status_header', array( $this, 'status_header' ), 10, 2 ); 641 | add_filter( 'wp_redirect_status', array( $this, 'redirect_status' ), 10, 2 ); 642 | 643 | // Start the spidey-sense listening 644 | ob_start( array( $this, 'ob' ) ); 645 | } 646 | 647 | /** 648 | * Setup the query attribute if a query string exists 649 | * 650 | * @since 6.0.0 651 | */ 652 | private function setup_query() { 653 | 654 | // Things that define a unique page. 655 | if ( isset( $_SERVER['QUERY_STRING'] ) ) { 656 | parse_str( $_SERVER['QUERY_STRING'], $this->query ); 657 | } 658 | } 659 | 660 | /** 661 | * Setup the unique attribute based on the server protocol 662 | * 663 | * @since 6.0.0 664 | */ 665 | private function setup_protocol() { 666 | 667 | // Build different versions for HTTP/1.1 and HTTP/2.0 668 | $protocol = wp_get_server_protocol(); 669 | if ( 'HTTP/1.0' !== $protocol ) { 670 | $this->unique['server_protocol'] = $protocol; 671 | } 672 | } 673 | 674 | /** 675 | * Setup the keys array attribute 676 | * 677 | * @since 6.0.0 678 | */ 679 | private function setup_keys() { 680 | 681 | // Get the path 682 | $path = ( $_pos = strpos( $_SERVER['REQUEST_URI'], '?' ) ) 683 | ? substr( $_SERVER['REQUEST_URI'], 0, $_pos ) 684 | : $_SERVER['REQUEST_URI']; 685 | 686 | // Setup keys 687 | $this->keys = array( 688 | 'host' => $_SERVER['HTTP_HOST'], 689 | 'method' => $_SERVER['REQUEST_METHOD'], 690 | 'path' => $path, 691 | 'query' => $this->query, 692 | 'extra' => $this->unique, 693 | 'ssl' => is_ssl() 694 | ); 695 | } 696 | 697 | /** 698 | * Setup the URL/permalink attributes. 699 | * 700 | * Must be called after setup_keys() 701 | * 702 | * @since 6.0.0 703 | */ 704 | private function setup_url() { 705 | 706 | // Get HTTP scheme 707 | $scheme = ( true === $this->keys['ssl'] ) 708 | ? 'https://' 709 | : 'http://'; 710 | 711 | // Maybe get post query string 712 | $p = isset( $this->keys['query']['p'] ) 713 | ? '?p=' . $this->keys['query']['p'] 714 | : ''; 715 | 716 | // Recreate the permalink from the URL 717 | $this->permalink = $scheme . $this->keys['host'] . $this->keys['path'] . $p; 718 | $this->url_key = md5( $this->permalink ); 719 | $this->url_version = (int) wp_cache_get( "{$this->url_key}_version", $this->group ); 720 | } 721 | 722 | /** 723 | * Populate the cache variable, and maybe add/increment related caches. 724 | * 725 | * @since 6.0.0 726 | */ 727 | private function maybe_update_cache() { 728 | 729 | // Get the spider_cache 730 | $this->cache = wp_cache_get( $this->key, $this->group ); 731 | 732 | // Are we only caching frequently-requested pages? 733 | if ( ( $this->seconds < 1 ) || ( $this->times < 2 ) ) { 734 | $this->do = true; 735 | 736 | // No spider_cache item found, or ready to sample traffic again at 737 | // the end of the spider_cache life? 738 | } elseif ( ! is_array( $this->cache ) || ( $this->started >= ( $this->cache['time'] + $this->max_age - $this->seconds ) ) ) { 739 | wp_cache_add( $this->req_key, 0, $this->group ); 740 | 741 | $this->requests = wp_cache_incr( $this->req_key, 1, $this->group ); 742 | $this->do = (bool) ( $this->requests >= $this->times ); 743 | } 744 | 745 | // If the document has been updated and we are the first to notice, regenerate it. 746 | if ( ( true === $this->do ) && isset( $this->cache['version'] ) && ( $this->cache['version'] < $this->url_version ) ) { 747 | $this->genlock = wp_cache_add( "{$this->url_key}_genlock", 1, $this->group, 10 ); 748 | } 749 | } 750 | 751 | /** 752 | * Maybe output the contents from cache, including performing a redirection 753 | * if necessary. 754 | * 755 | * @since 6.0.0 756 | * 757 | * @return void 758 | */ 759 | private function maybe_output_cache() { 760 | 761 | // Bail if no page, or is locked or expired 762 | if ( ! isset( $this->cache['time'] ) || ( true === $this->genlock ) || ( $this->started > ( $this->cache['time'] + $this->cache['max_age'] ) ) ) { 763 | return; 764 | } 765 | 766 | // Maybe perform a redirection 767 | $this->maybe_do_redirect(); 768 | 769 | // Use the spider_cache save time for Last-Modified so we can issue 770 | // "304 Not Modified" but don't clobber a cached Last-Modified header. 771 | if ( ( true === $this->cache_control ) && ! isset( $this->cache['headers']['Last-Modified'][0] ) ) { 772 | @header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this->cache['time'] ) . ' GMT', true ); 773 | @header( 'Cache-Control: max-age=' . ( $this->cache['max_age'] - $this->started + $this->cache['time'] ) . ', must-revalidate', true ); 774 | } 775 | 776 | // Add some debug info just before 777 | if ( true === $this->debug ) { 778 | $this->add_debug_from_cache(); 779 | } 780 | 781 | $this->do_headers( $this->headers, $this->cache['headers'] ); 782 | 783 | // Bail if not modified 784 | if ( $this->not_modified() ) { 785 | @header( "HTTP/1.1 304 Not Modified", true, 304 ); 786 | die; 787 | } 788 | 789 | // Set header if cached 790 | if ( ! empty( $this->cache['status_header'] ) ) { 791 | @header( $this->cache['status_header'], true ); 792 | } 793 | 794 | // Have you ever heard a death rattle before? 795 | die( $this->cache['output'] ); 796 | } 797 | 798 | /** 799 | * Maybe perform a redirection 800 | * 801 | * @since 6.0.0 802 | */ 803 | private function maybe_do_redirect() { 804 | 805 | // Issue redirect if cached and enabled 806 | if ( $this->cache['redirect_status'] && $this->cache['redirect_location'] && $this->cache_redirects ) { 807 | 808 | // Do headers 809 | $this->do_headers( $this->headers ); 810 | 811 | // From vars.php 812 | $is_IIS = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer' ) !== false ); 813 | 814 | // IIS 815 | if ( ! empty( $is_IIS ) ) { 816 | @header( "Refresh: 0;url={$this->cache['redirect_location']}" ); 817 | } else { 818 | if ( php_sapi_name() !== 'cgi-fcgi' ) { 819 | $texts = array( 820 | 300 => 'Multiple Choices', 821 | 301 => 'Moved Permanently', 822 | 302 => 'Found', 823 | 303 => 'See Other', 824 | 304 => 'Not Modified', 825 | 305 => 'Use Proxy', 826 | 306 => 'Reserved', 827 | 307 => 'Temporary Redirect', 828 | ); 829 | 830 | // Get the protocol 831 | $protocol = wp_get_server_protocol(); 832 | 833 | // Found/Redirect header 834 | isset( $texts[ $this->cache['redirect_status'] ] ) 835 | ? @header( "{$protocol} {$this->cache['redirect_status']} {$texts[ $this->cache['redirect_status'] ]}" ) 836 | : @header( "{$protocol} 302 Found" ); 837 | } 838 | 839 | @header( "Location: {$this->cache['redirect_location']}" ); 840 | } 841 | 842 | // Exit so redirect takes effect 843 | exit; 844 | } 845 | } 846 | 847 | /** 848 | * Has the cached page changed? 849 | * 850 | * @since 6.0.0 851 | * 852 | * @return boolean 853 | */ 854 | private function not_modified() { 855 | 856 | // Default value 857 | $three_oh_four = false; 858 | 859 | // Respect ETags served with feeds. 860 | if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) && isset( $this->cache['headers']['ETag'][0] ) && ( $_SERVER['HTTP_IF_NONE_MATCH'] == $this->cache['headers']['ETag'][0] ) ) { 861 | $three_oh_four = true; 862 | 863 | // Respect If-Modified-Since. 864 | } elseif ( ( true === $this->cache_control ) && isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { 865 | 866 | // Get times 867 | $client_time = strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); 868 | $cache_time = isset( $this->cache['headers']['Last-Modified'][0] ) 869 | ? strtotime( $this->cache['headers']['Last-Modified'][0] ) 870 | : $this->cache['time']; 871 | 872 | // Maybe 304 873 | if ( $client_time >= $cache_time ) { 874 | $three_oh_four = true; 875 | } 876 | } 877 | 878 | // Return 304 status 879 | return (bool) $three_oh_four; 880 | } 881 | 882 | /** 883 | * Look for predefined constants, and add them to the skip_cookies array 884 | * 885 | * @since 2.3.0 886 | */ 887 | private function set_skip_cookies() { 888 | 889 | // Auth cookie 890 | if ( defined( 'AUTH_COOKIE' ) && AUTH_COOKIE && ! in_array( AUTH_COOKIE, $this->skip_cookies, true ) ) { 891 | $this->skip_cookies[] = AUTH_COOKIE; 892 | } 893 | 894 | // User cookie 895 | if ( defined( 'USER_COOKIE' ) && USER_COOKIE && ! in_array( USER_COOKIE, $this->skip_cookies, true ) ) { 896 | $this->skip_cookies[] = USER_COOKIE; 897 | } 898 | 899 | // Logged-in cookie 900 | if ( defined( 'LOGGED_IN_COOKIE' ) && LOGGED_IN_COOKIE && ! in_array( LOGGED_IN_COOKIE, $this->skip_cookies, true ) ) { 901 | $this->skip_cookies[] = LOGGED_IN_COOKIE; 902 | } 903 | } 904 | 905 | /** 906 | * Return whether or not user related cookies have been detected 907 | * 908 | * @since 2.3.0 909 | * 910 | * @return boolean 911 | */ 912 | private function has_cookies() { 913 | 914 | // Bail if cookies indicate a cache-exempt visitor 915 | if ( empty( $_COOKIE ) || ! is_array( $_COOKIE ) ) { 916 | return false; 917 | } 918 | 919 | // Set cookies used to skip 920 | $this->set_skip_cookies(); 921 | 922 | // Get cookie keys 923 | $cookie_keys = array_keys( $_COOKIE ); 924 | 925 | // Loop through keys 926 | foreach ( $cookie_keys as $cookie_key ) { 927 | 928 | // Skip cookie and keep caching 929 | if ( in_array( $cookie_key, $this->noskip_cookies, true ) ) { 930 | continue; 931 | } 932 | 933 | // Don't skip cookie, and don't cache 934 | if ( in_array( $cookie_key, $this->skip_cookies, true ) ) { 935 | return true; 936 | } 937 | 938 | // Comment cookie, so don't cache 939 | if ( substr( $cookie_key, 0, 14 ) === 'comment_author' ) { 940 | return true; 941 | } 942 | 943 | // Generic 'wordpress' cookies (that are not test cookies) 944 | if ( ( substr( $cookie_key, 0, 9 ) === 'wordpress' ) && ( $cookie_key !== 'wordpress_test_cookie' ) ) { 945 | return true; 946 | } 947 | } 948 | 949 | // No cookies 950 | return false; 951 | } 952 | 953 | /** 954 | * Defined here because timer_stop() calls number_format_i18n() 955 | * 956 | * @since 2.0.0 957 | */ 958 | private function timer_stop( $display = true, $precision = 3 ) { 959 | global $timestart, $timeend; 960 | 961 | $mtime = microtime(); 962 | $mtime = explode( ' ', $mtime ); 963 | $timeend = $mtime[1] + $mtime[0]; 964 | $timetotal = $timeend - $timestart; 965 | $r = number_format( $timetotal, $precision ); 966 | 967 | if ( true === $display ) { 968 | echo $r; 969 | } 970 | 971 | return $r; 972 | } 973 | } 974 | -------------------------------------------------------------------------------- /wp-spider-cache/includes/class-var-dump.php: -------------------------------------------------------------------------------- 1 | {$output}"; 75 | } 76 | 77 | /** 78 | * Internally format the variable being dumped. 79 | * 80 | * @since 0.1.0 81 | * 82 | * @param string $var 83 | * @param boolean $format 84 | * 85 | * @return string 86 | */ 87 | private static function format( $var = '', $format = false ) { 88 | 89 | // Start the output buffering 90 | ob_start(); 91 | 92 | // Generate the output 93 | var_dump( $var ); 94 | 95 | // Get var_dump output from the buffer 96 | $buffer = ob_get_clean(); 97 | 98 | // No new lines after array/object items 99 | $output = str_replace( "=>\n", '=>', $buffer ); 100 | 101 | // No stray whitespace before type castings 102 | $output = preg_replace( '/=>\s+/', '=> ', $output ); 103 | 104 | // Handle formatting on our own 105 | if ( true === $format ) { 106 | 107 | // Regex to wrap words with HTML 108 | $maps = array( 109 | 'string' => '/(string\((?P\d+)\)) (?P\"(? '/\[\"(?P.+)\"(?:\:\"(?P[a-z0-9_\\\]+)\")?(?:\:(?Ppublic|protected|private))?\]=>/Ui', 111 | 'countable' => '/(?Parray|int|string)\((?P\d+)\)/', 112 | 'resource' => '/resource\((?P\d+)\) of type \((?P[a-z0-9_\\\]+)\)/', 113 | 'bool' => '/bool\((?Ptrue|false)\)/', 114 | 'float' => '/float\((?P[0-9\.]+)\)/', 115 | 'object' => '/object\((?P[a-z_\\\]+)\)\#(?P\d+) \((?P\d+)\)/i', 116 | ); 117 | 118 | // Loop through maps & replace with callback 119 | foreach ( $maps as $function => $pattern ) { 120 | $output = preg_replace_callback( $pattern, array( __CLASS__, 'process_' . $function ), $output ); 121 | } 122 | } 123 | 124 | return $output; 125 | } 126 | 127 | /** 128 | * Maybe trim spaces off of a value. 129 | * 130 | * @since 0.1.0 131 | * 132 | * @param mixed $value 133 | * @return mixed 134 | */ 135 | public static function maybe_trim( $value ) { 136 | return is_string( $value ) 137 | ? trim( $value ) 138 | : $value; 139 | } 140 | 141 | /** 142 | * Maybe escape a value. 143 | * 144 | * @since 0.1.0 145 | * 146 | * @param mixed $value 147 | * @return mixed 148 | */ 149 | public static function maybe_escape( $value ) { 150 | return is_int( $value ) 151 | ? (int) $value 152 | : esc_html( $value ); 153 | } 154 | 155 | /** 156 | * Maybe truncate a value. 157 | * 158 | * @since 0.1.0 159 | * 160 | * @param mixed $value 161 | * @return mixed 162 | */ 163 | public static function maybe_truncate( $value ) { 164 | return is_string( $value ) && ( strlen( $value ) > 500 ) 165 | ? substr( $value, 0, 497 ) . '...' 166 | : $value; 167 | } 168 | 169 | /** 170 | * Process strings 171 | * 172 | * @since 0.1.0 173 | * 174 | * @param array $matches Matches from preg_* 175 | * @return string 176 | */ 177 | private static function process_string( array $matches ) { 178 | $length = 'string(' . esc_html( $matches[ 'length' ] ) . ')'; 179 | $value = '' . esc_html( $matches[ 'value' ] ) . ''; 180 | return $length . ' ' . $value; 181 | } 182 | 183 | /** 184 | * Process arrays 185 | * 186 | * @since 0.1.0 187 | * 188 | * @param array $matches Matches from preg_* 189 | * @return string 190 | */ 191 | private static function process_array( array $matches ) { 192 | 193 | // prepare the key name 194 | $key = '"' . esc_html( $matches[ 'key' ] ) . '"'; 195 | $class = ''; 196 | $scope = ''; 197 | 198 | // Prepare the parent class name 199 | if ( ! empty( $matches[ 'class' ] ) ) { 200 | $class = ':"' . esc_html( $matches[ 'class' ] ) . '"'; 201 | } 202 | 203 | // Prepare the scope indicator 204 | if ( ! empty( $matches[ 'scope' ] ) ) { 205 | $scope = ':' . esc_html( $matches[ 'scope' ] ) . ''; 206 | } 207 | 208 | // return the final string 209 | return '[' . $key . $class . $scope . '] ' . esc_html( '=>' ); 210 | } 211 | 212 | /** 213 | * Process countables 214 | * 215 | * @since 0.1.0 216 | * 217 | * @param array $matches Matches from preg_* 218 | * 219 | * @return string 220 | */ 221 | private static function process_countable( array $matches ) { 222 | $type = '' . esc_html( $matches[ 'type' ] ) . ''; 223 | $count = '(' . esc_html( $matches[ 'count' ] ) . ')'; 224 | 225 | return $type . $count; 226 | } 227 | 228 | /** 229 | * Process boolean values 230 | * 231 | * @since 0.1.0 232 | * 233 | * @param array $matches Matches from preg_* 234 | * @return string 235 | */ 236 | private static function process_bool( array $matches ) { 237 | return 'bool(' . esc_html( $matches[ 'value' ] ) . ')'; 238 | } 239 | 240 | /** 241 | * Process floats 242 | * 243 | * @since 0.1.0 244 | * 245 | * @param array $matches Matches from preg_* 246 | * @return string 247 | */ 248 | private static function process_float( array $matches ) { 249 | return 'float(' . esc_html( $matches[ 'value' ] ) . ')'; 250 | } 251 | 252 | /** 253 | * Process resources 254 | * 255 | * @since 0.1.0 256 | * 257 | * @param array $matches Matches from preg_* 258 | * @return string 259 | */ 260 | private static function process_resource( array $matches ) { 261 | $count = 'resource(' . esc_html( $matches[ 'count' ] ) . ')'; 262 | $type = ' of type (' . esc_html( $matches[ 'class' ] ) . ')'; 263 | return $count . $type; 264 | } 265 | 266 | /** 267 | * Process objects 268 | * 269 | * @since 0.1.0 270 | * 271 | * @param array $matches Matches from preg_* 272 | * @return string 273 | */ 274 | private static function process_object( array $matches ) { 275 | $class = 'object(' . esc_html( $matches[ 'class' ] ) . ')#' . esc_html( $matches[ 'id' ] ); 276 | $count = '(' . esc_html( $matches[ 'count' ] ) . ')'; 277 | return $class . $count; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /wp-spider-cache/includes/functions.php: -------------------------------------------------------------------------------- 1 | add( $key, $value, $group, $expiration ); 37 | } 38 | 39 | /** 40 | * Adds a value to cache on a specific server. 41 | * 42 | * Using a server_key value, the object can be stored on a specified server as opposed 43 | * to a random server in the stack. Note that this method will add the key/value to the 44 | * _cache object as part of the runtime cache. It will add it to an array for the 45 | * specified server_key. 46 | * 47 | * @since 2.0.0 48 | * 49 | * @link http://www.php.net/manual/en/memcached.addbykey.php 50 | * 51 | * @param string $server_key The key identifying the server to store the value on. 52 | * @param string $key The key under which to store the value. 53 | * @param mixed $value The value to store. 54 | * @param string $group The group value appended to the $key. 55 | * @param int $expiration The expiration time, defaults to 0. 56 | * @return bool Returns TRUE on success or FALSE on failure. 57 | */ 58 | function wp_cache_add_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) { 59 | return wp_object_cache()->addByKey( $server_key, $key, $value, $group, $expiration ); 60 | } 61 | 62 | /** 63 | * Add a single server to the list of cache servers. 64 | * 65 | * @link http://www.php.net/manual/en/memcached.addserver.php 66 | * 67 | * @param string $host The hostname of the memcache server. 68 | * @param int $port The port on which memcache is running. 69 | * @param int $weight The weight of the server relative to the total weight of all the servers in the pool. 70 | * @return bool Returns TRUE on success or FALSE on failure. 71 | */ 72 | function wp_cache_add_server( $host, $port, $weight = 0 ) { 73 | return wp_object_cache()->addServer( $host, $port, $weight ); 74 | } 75 | 76 | /** 77 | * Adds an array of servers to the pool. 78 | * 79 | * Each individual server in the array must include a domain and port, with an optional 80 | * weight value: $servers = array( array( '127.0.0.1', 11211, 0 ) ); 81 | * 82 | * @since 2.0.0 83 | * 84 | * @link http://www.php.net/manual/en/memcached.addservers.php 85 | * 86 | * @param array $servers Array of server to register. 87 | * @return bool True on success; false on failure. 88 | */ 89 | function wp_cache_add_servers( $servers ) { 90 | return wp_object_cache()->addServers( $servers ); 91 | } 92 | 93 | /** 94 | * Append data to an existing item. 95 | * 96 | * This method should throw an error if it is used with compressed data. This 97 | * is an expected behavior. Memcached casts the value to be appended to the initial value to the 98 | * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to 99 | * how memcached treats types, the behavior has been mimicked in the internal cache to produce 100 | * similar results and improve consistency. It is recommend that appends only occur with data of 101 | * the same type. 102 | * 103 | * @since 2.0.0 104 | * 105 | * @link http://www.php.net/manual/en/memcached.append.php 106 | * 107 | * @param string $key The key under which to store the value. 108 | * @param mixed $value Must be string as appending mixed values is not well-defined 109 | * @param string $group The group value appended to the $key. 110 | * @return bool Returns TRUE on success or FALSE on failure. 111 | */ 112 | function wp_cache_append( $key, $value, $group = '' ) { 113 | return wp_object_cache()->append( $key, $value, $group ); 114 | } 115 | 116 | /** 117 | * Append data to an existing item by server key. 118 | * 119 | * This method should throw an error if it is used with compressed data. This 120 | * is an expected behavior. Memcached casts the value to be appended to the initial value to the 121 | * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to 122 | * how memcached treats types, the behavior has been mimicked in the internal cache to produce 123 | * similar results and improve consistency. It is recommend that appends only occur with data of 124 | * the same type. 125 | * 126 | * @since 2.0.0 127 | * 128 | * @link http://www.php.net/manual/en/memcached.appendbykey.php 129 | * 130 | * @param string $server_key The key identifying the server to store the value on. 131 | * @param string $key The key under which to store the value. 132 | * @param mixed $value Must be string as appending mixed values is not well-defined 133 | * @param string $group The group value appended to the $key. 134 | * @return bool Returns TRUE on success or FALSE on failure. 135 | */ 136 | function wp_cache_append_by_key( $server_key, $key, $value, $group = '' ) { 137 | return wp_object_cache()->appendByKey( $server_key, $key, $value, $group ); 138 | } 139 | 140 | /** 141 | * Performs a "check and set" to store data. 142 | * 143 | * The set will be successful only if the no other request has updated the value since it was fetched by 144 | * this request. 145 | * 146 | * @since 2.0.0 147 | * 148 | * @link http://www.php.net/manual/en/memcached.cas.php 149 | * 150 | * @param float $cas_token Unique value associated with the existing item. Generated by memcached. 151 | * @param string $key The key under which to store the value. 152 | * @param mixed $value The value to store. 153 | * @param string $group The group value appended to the $key. 154 | * @param int $expiration The expiration time, defaults to 0. 155 | * @return bool Returns TRUE on success or FALSE on failure. 156 | */ 157 | function wp_cache_cas( $cas_token, $key, $value, $group = '', $expiration = 0 ) { 158 | return wp_object_cache()->cas( $cas_token, $key, $value, $group, $expiration ); 159 | } 160 | 161 | /** 162 | * Performs a "check and set" to store data with a server key. 163 | * 164 | * The set will be successful only if the no other request has updated the value since it was fetched by 165 | * this request. 166 | * 167 | * @since 2.0.0 168 | * 169 | * @link http://www.php.net/manual/en/memcached.casbykey.php 170 | * 171 | * @param float $cas_token Unique value associated with the existing item. Generated by memcached. 172 | * @param string $server_key The key identifying the server to store the value on. 173 | * @param string $key The key under which to store the value. 174 | * @param mixed $value The value to store. 175 | * @param string $group The group value appended to the $key. 176 | * @param int $expiration The expiration time, defaults to 0. 177 | * @return bool Returns TRUE on success or FALSE on failure. 178 | */ 179 | function wp_cache_cas_by_key( $cas_token, $server_key, $key, $value, $group = '', $expiration = 0 ) { 180 | return wp_object_cache()->casByKey( $cas_token, $server_key, $key, $value, $group, $expiration ); 181 | } 182 | 183 | /** 184 | * Closes the cache. 185 | * 186 | * This function is used to close a connection to a cache server. 187 | * 188 | * @since 2.0.0 189 | * 190 | * @return bool Return TRUE on success or FALSE on failure. 191 | */ 192 | function wp_cache_close() { 193 | return wp_object_cache()->close(); 194 | } 195 | 196 | /** 197 | * Connect to a cache serve 198 | * 199 | * This function is used to create a connection to a cache server. 200 | * 201 | * @since 2.2.0 202 | * 203 | * @return bool Return TRUE on success or FALSE on failure. 204 | */ 205 | function wp_cache_connect( $server = '127.0.0.1', $port = 11211 ) { 206 | return wp_object_cache()->connect( $server, $port ); 207 | } 208 | 209 | /** 210 | * Decrement a numeric item's value. 211 | * 212 | * @since 2.0.0 213 | * 214 | * @link http://www.php.net/manual/en/memcached.decrement.php 215 | * 216 | * @param string $key The key under which to store the value. 217 | * @param int $offset The amount by which to decrement the item's value. 218 | * @param string $group The group value appended to the $key. 219 | * @return int|bool Returns item's new value on success or FALSE on failure. 220 | */ 221 | function wp_cache_decrement( $key, $offset = 1, $group = '' ) { 222 | return wp_object_cache()->decrement( $key, $offset, $group ); 223 | } 224 | 225 | /** 226 | * Decrement a numeric item's value. 227 | * 228 | * Same as wp_cache_decrement. Original WordPress caching backends use wp_cache_decr. I 229 | * want both spellings to work. 230 | * 231 | * @since 2.0.0 232 | * 233 | * @link http://www.php.net/manual/en/memcached.decrement.php 234 | * 235 | * @param string $key The key under which to store the value. 236 | * @param int $offset The amount by which to decrement the item's value. 237 | * @param string $group The group value appended to the $key. 238 | * @return int|bool Returns item's new value on success or FALSE on failure. 239 | */ 240 | function wp_cache_decr( $key, $offset = 1, $group = '' ) { 241 | return wp_cache_decrement( $key, $offset, $group ); 242 | } 243 | 244 | /** 245 | * Remove the item from the cache. 246 | * 247 | * Remove an item from memcached with identified by $key after $time seconds. The 248 | * $time parameter allows an object to be queued for deletion without immediately 249 | * deleting. Between the time that it is queued and the time it's deleted, add, 250 | * replace, and get will fail, but set will succeed. 251 | * 252 | * @since 2.0.0 253 | * 254 | * @link http://www.php.net/manual/en/memcached.delete.php 255 | * 256 | * @param string $key The key under which to store the value. 257 | * @param string $group The group value appended to the $key. 258 | * @param int $time The amount of time the server will wait to delete the item in seconds. 259 | * @return bool Returns TRUE on success or FALSE on failure. 260 | */ 261 | function wp_cache_delete( $key, $group = '', $time = 0 ) { 262 | return wp_object_cache()->delete( $key, $group, $time ); 263 | } 264 | 265 | /** 266 | * Remove the item from the cache by server key. 267 | * 268 | * Remove an item from memcached with identified by $key after $time seconds. The 269 | * $time parameter allows an object to be queued for deletion without immediately 270 | * deleting. Between the time that it is queued and the time it's deleted, add, 271 | * replace, and get will fail, but set will succeed. 272 | * 273 | * @since 2.0.0 274 | * 275 | * @link http://www.php.net/manual/en/memcached.deletebykey.php 276 | * 277 | * @param string $server_key The key identifying the server to store the value on. 278 | * @param string $key The key under which to store the value. 279 | * @param string $group The group value appended to the $key. 280 | * @param int $time The amount of time the server will wait to delete the item in seconds. 281 | * @return bool Returns TRUE on success or FALSE on failure. 282 | */ 283 | function wp_cache_delete_by_key( $server_key, $key, $group = '', $time = 0 ) { 284 | return wp_object_cache()->deleteByKey( $server_key, $key, $group, $time ); 285 | } 286 | 287 | /** 288 | * Fetch the next result. 289 | * 290 | * @since 2.0.0 291 | * 292 | * @link http://www.php.net/manual/en/memcached.fetch.php 293 | * 294 | * @return array|bool Returns the next result or FALSE otherwise. 295 | */ 296 | function wp_cache_fetch() { 297 | return wp_object_cache()->fetch(); 298 | } 299 | 300 | /** 301 | * Fetch all remaining results from the last request. 302 | * 303 | * @since 2.0.0 304 | * 305 | * @link http://www.php.net/manual/en/memcached.fetchall.php 306 | * 307 | * @return array|bool Returns the results or FALSE on failure. 308 | */ 309 | function wp_cache_fetch_all() { 310 | return wp_object_cache()->fetchAll(); 311 | } 312 | 313 | /** 314 | * Invalidate all items in the cache. 315 | * 316 | * @since 2.0.0 317 | * 318 | * @link http://www.php.net/manual/en/memcached.flush.php 319 | * 320 | * @param int $delay Number of seconds to wait before invalidating the items. 321 | * @return bool Returns TRUE on success or FALSE on failure. 322 | */ 323 | function wp_cache_flush( $delay = 0 ) { 324 | return wp_object_cache()->flush( $delay ); 325 | } 326 | 327 | /** 328 | * Retrieve object from cache. 329 | * 330 | * Gets an object from cache based on $key and $group. In order to fully support the $cache_cb and $cas_token 331 | * parameters, the runtime cache is ignored by this function if either of those values are set. If either of 332 | * those values are set, the request is made directly to the memcached server for proper handling of the 333 | * callback and/or token. 334 | * 335 | * Note that the $deprecated and $found args are only here for compatibility with the native wp_cache_get function. 336 | * 337 | * @since 2.0.0 338 | * 339 | * @link http://www.php.net/manual/en/memcached.get.php 340 | * 341 | * @param string $key The key under which to store the value. 342 | * @param string $group The group value appended to the $key. 343 | * @param bool $force Whether or not to force a cache invalidation. 344 | * @param null|bool $found Variable passed by reference to determine if the value was found or not. 345 | * @param null|string $cache_cb Read-through caching callback. 346 | * @param null|float $cas_token The variable to store the CAS token in. 347 | * @return bool|mixed Cached object value. 348 | */ 349 | function wp_cache_get( $key, $group = '', $force = false, &$found = null, $cache_cb = null, &$cas_token = null ) { 350 | if ( func_num_args() > 4 ) { 351 | return wp_object_cache()->get( $key, $group, $force, $found, '', false, $cache_cb, $cas_token ); 352 | } else { 353 | return wp_object_cache()->get( $key, $group, $force, $found ); 354 | } 355 | } 356 | 357 | /** 358 | * Retrieve object from cache from specified server. 359 | * 360 | * Gets an object from cache based on $key, $group and $server_key. In order to fully support the $cache_cb and $cas_token 361 | * parameters, the runtime cache is ignored by this function if either of those values are set. If either of 362 | * those values are set, the request is made directly to the memcached server for proper handling of the 363 | * callback and/or token. 364 | * 365 | * @since 2.0.0 366 | * 367 | * @link http://www.php.net/manual/en/memcached.getbykey.php 368 | * 369 | * @param string $server_key The key identifying the server to store the value on. 370 | * @param string $key The key under which to store the value. 371 | * @param string $group The group value appended to the $key. 372 | * @param bool $force Whether or not to force a cache invalidation. 373 | * @param null|bool $found Variable passed by reference to determine if the value was found or not. 374 | * @param null|string $cache_cb Read-through caching callback. 375 | * @param null|float $cas_token The variable to store the CAS token in. 376 | * @return bool|mixed Cached object value. 377 | */ 378 | function wp_cache_get_by_key( $server_key, $key, $group = '', $force = false, &$found = null, $cache_cb = NULL, &$cas_token = NULL ) { 379 | if ( func_num_args() > 5 ) { 380 | return wp_object_cache()->getByKey( $server_key, $key, $group, $force, $found, $cache_cb, $cas_token ); 381 | } else { 382 | return wp_object_cache()->getByKey( $server_key, $key, $group, $force, $found ); 383 | } 384 | } 385 | 386 | /** 387 | * Request multiple keys without blocking. 388 | * 389 | * @since 2.0.0 390 | * 391 | * @link http://www.php.net/manual/en/memcached.getdelayed.php 392 | * 393 | * @param string|array $keys Array or string of key(s) to request. 394 | * @param string|array $groups Array or string of group(s) for the key(s). See buildKeys for more on how these are handled. 395 | * @param bool $with_cas Whether to request CAS token values also. 396 | * @param null $value_cb The result callback or NULL. 397 | * @return bool Returns TRUE on success or FALSE on failure. 398 | */ 399 | function wp_cache_get_delayed( $keys, $groups = '', $with_cas = false, $value_cb = NULL ) { 400 | return wp_object_cache()->getDelayed( $keys, $groups, $with_cas, $value_cb ); 401 | } 402 | 403 | /** 404 | * Request multiple keys without blocking from a specified server. 405 | * 406 | * @link http://www.php.net/manual/en/memcached.getdelayed.php 407 | * 408 | * @param string $server_key The key identifying the server to store the value on. 409 | * @param string|array $keys Array or string of key(s) to request. 410 | * @param string|array $groups Array or string of group(s) for the key(s). See buildKeys for more on how these are handled. 411 | * @param bool $with_cas Whether to request CAS token values also. 412 | * @param null $value_cb The result callback or NULL. 413 | * @return bool Returns TRUE on success or FALSE on failure. 414 | */ 415 | function wp_cache_get_delayed_by_key( $server_key, $keys, $groups = '', $with_cas = false, $value_cb = NULL ) { 416 | return wp_object_cache()->getDelayedByKey( $server_key, $keys, $groups, $with_cas, $value_cb ); 417 | } 418 | 419 | /** 420 | * Get extended server pool statistics. 421 | * 422 | * @since 2.2.0 423 | * 424 | * @link http://www.php.net/manual/en/memcached.getstats.php 425 | * 426 | * @return array Array of server statistics, one entry per server. 427 | */ 428 | function wp_cache_get_extended_stats( $type, $slab_id = 0, $limit = 100) { 429 | return wp_object_cache()->getExtendedStats( $type, $slab_id, $limit); 430 | } 431 | 432 | /** 433 | * Retrieve a cache key based on key & group. 434 | * 435 | * @since 2.0.0 436 | * 437 | * @link http://www.php.net/manual/en/memcached.get.php 438 | * 439 | * @param string $key The key under which a value is stored. 440 | * @param string $group The group value appended to the $key. 441 | * @return string Returns the cache key used for getting & setting. 442 | */ 443 | function wp_cache_get_key( $key, $group = '' ) { 444 | return wp_object_cache()->buildKey( $key, $group ); 445 | } 446 | 447 | /** 448 | * Gets multiple values from memcached in one request. 449 | * 450 | * See the buildKeys method definition to understand the $keys/$groups parameters. 451 | * 452 | * @since 2.0.0 453 | * 454 | * @link http://www.php.net/manual/en/memcached.getmulti.php 455 | * 456 | * @param array $keys Array of keys to retrieve. 457 | * @param string|array $groups If string, used for all keys. If arrays, corresponds with the $keys array. 458 | * @param null|array $cas_tokens The variable to store the CAS tokens for the found items. 459 | * @param int $flags The flags for the get operation. 460 | * @return bool|array Returns the array of found items or FALSE on failure. 461 | */ 462 | function wp_cache_get_multi( $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) { 463 | if ( func_num_args() > 2 ) { 464 | return wp_object_cache()->getMulti( $keys, $groups, '', $cas_tokens, $flags ); 465 | } else { 466 | return wp_object_cache()->getMulti( $keys, $groups ); 467 | } 468 | } 469 | 470 | /** 471 | * Gets multiple values from memcached in one request by specified server key. 472 | * 473 | * See the buildKeys method definition to understand the $keys/$groups parameters. 474 | * 475 | * @since 2.0.0 476 | * 477 | * @link http://www.php.net/manual/en/memcached.getmultibykey.php 478 | * 479 | * @param string $server_key The key identifying the server to store the value on. 480 | * @param array $keys Array of keys to retrieve. 481 | * @param string|array $groups If string, used for all keys. If arrays, corresponds with the $keys array. 482 | * @param null|array $cas_tokens The variable to store the CAS tokens for the found items. 483 | * @param int $flags The flags for the get operation. 484 | * @return bool|array Returns the array of found items or FALSE on failure. 485 | */ 486 | function wp_cache_get_multi_by_key( $server_key, $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) { 487 | if ( func_num_args() > 3 ) { 488 | return wp_object_cache()->getMultiByKey( $server_key, $keys, $groups, $cas_tokens, $flags ); 489 | } else { 490 | return wp_object_cache()->getMultiByKey( $server_key, $keys, $groups ); 491 | } 492 | } 493 | 494 | /** 495 | * Retrieve a Memcached option value. 496 | * 497 | * @since 2.0.0 498 | * 499 | * @link http://www.php.net/manual/en/memcached.getoption.php 500 | * 501 | * @param int $option One of the Memcached::OPT_* constants. 502 | * @return mixed Returns the value of the requested option, or FALSE on error. 503 | */ 504 | function wp_cache_get_option( $option ) { 505 | return wp_object_cache()->getOption( $option ); 506 | } 507 | 508 | /** 509 | * Return the result code of the last option. 510 | * 511 | * @since 2.0.0 512 | * 513 | * @link http://www.php.net/manual/en/memcached.getresultcode.php 514 | * 515 | * @return int Result code of the last Memcached operation. 516 | */ 517 | function wp_cache_get_result_code() { 518 | return wp_object_cache()->getResultCode(); 519 | } 520 | 521 | /** 522 | * Return the message describing the result of the last operation. 523 | * 524 | * @since 2.0.0 525 | * 526 | * @link http://www.php.net/manual/en/memcached.getresultmessage.php 527 | * 528 | * @return string Message describing the result of the last Memcached operation. 529 | */ 530 | function wp_cache_get_result_message() { 531 | return wp_object_cache()->getResultMessage(); 532 | } 533 | 534 | /** 535 | * Get server information by key. 536 | * 537 | * @since 2.0.0 538 | * 539 | * @link http://www.php.net/manual/en/memcached.getserverbykey.php 540 | * 541 | * @param string $server_key The key identifying the server to store the value on. 542 | * @return array Array with host, post, and weight on success, FALSE on failure. 543 | */ 544 | function wp_cache_get_server_by_key( $server_key ) { 545 | return wp_object_cache()->getServerByKey( $server_key ); 546 | } 547 | 548 | /** 549 | * Get the list of servers in the pool. 550 | * 551 | * @since 2.0.0 552 | * 553 | * @link http://www.php.net/manual/en/memcached.getserverlist.php 554 | * 555 | * @return array The list of all servers in the server pool. 556 | */ 557 | function wp_cache_get_server_list() { 558 | return wp_object_cache()->getServerList(); 559 | } 560 | 561 | /** 562 | * Get server pool statistics. 563 | * 564 | * @since 2.0.0 565 | * 566 | * @link http://www.php.net/manual/en/memcached.getstats.php 567 | * 568 | * @return array Array of server statistics, one entry per server. 569 | */ 570 | function wp_cache_get_stats() { 571 | return wp_object_cache()->getStats(); 572 | } 573 | 574 | /** 575 | * Get server pool memcached version information. 576 | * 577 | * @since 2.0.0 578 | * 579 | * @link http://www.php.net/manual/en/memcached.getversion.php 580 | * 581 | * @return array Array of server versions, one entry per server. 582 | */ 583 | function wp_cache_get_version() { 584 | return wp_object_cache()->getVersion(); 585 | } 586 | 587 | /** 588 | * Increment a numeric item's value. 589 | * 590 | * @since 2.0.0 591 | * 592 | * @link http://www.php.net/manual/en/memcached.increment.php 593 | * 594 | * @param string $key The key under which to store the value. 595 | * @param int $offset The amount by which to increment the item's value. 596 | * @param string $group The group value appended to the $key. 597 | * @return int|bool Returns item's new value on success or FALSE on failure. 598 | */ 599 | function wp_cache_increment( $key, $offset = 1, $group = '' ) { 600 | return wp_object_cache()->increment( $key, $offset, $group ); 601 | } 602 | 603 | /** 604 | * Increment a numeric item's value. 605 | * 606 | * This is the same as wp_cache_increment, but kept for back compatibility. The original 607 | * WordPress caching backends use wp_cache_incr. I want both to work. 608 | * 609 | * @since 2.0.0 610 | ** 611 | * @link http://www.php.net/manual/en/memcached.increment.php 612 | * 613 | * @param string $key The key under which to store the value. 614 | * @param int $offset The amount by which to increment the item's value. 615 | * @param string $group The group value appended to the $key. 616 | * @return int|bool Returns item's new value on success or FALSE on failure. 617 | */ 618 | function wp_cache_incr( $key, $offset = 1, $group = '' ) { 619 | return wp_cache_increment( $key, $offset, $group ); 620 | } 621 | 622 | /** 623 | * Prepend data to an existing item. 624 | * 625 | * This method should throw an error if it is used with compressed data. This is an expected behavior. 626 | * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be 627 | * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to 628 | * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer" 629 | * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been 630 | * mimicked in the internal cache to produce similar results and improve consistency. It is recommend 631 | * that prepends only occur with data of the same type. 632 | * 633 | * @since 2.0.0 634 | * 635 | * @link http://www.php.net/manual/en/memcached.prepend.php 636 | * 637 | * @param string $key The key under which to store the value. 638 | * @param string $value Must be string as prepending mixed values is not well-defined. 639 | * @param string $group The group value prepended to the $key. 640 | * @return bool Returns TRUE on success or FALSE on failure. 641 | */ 642 | function wp_cache_prepend( $key, $value, $group = '' ) { 643 | return wp_object_cache()->prepend( $key, $value, $group ); 644 | } 645 | 646 | /** 647 | * Append data to an existing item by server key. 648 | * 649 | * This method should throw an error if it is used with compressed data. This is an expected behavior. 650 | * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be 651 | * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to 652 | * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer" 653 | * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been 654 | * mimicked in the internal cache to produce similar results and improve consistency. It is recommend 655 | * that prepends only occur with data of the same type. 656 | * 657 | * @since 2.0.0 658 | * 659 | * @link http://www.php.net/manual/en/memcached.prependbykey.php 660 | * 661 | * @param string $server_key The key identifying the server to store the value on. 662 | * @param string $key The key under which to store the value. 663 | * @param string $value Must be string as prepending mixed values is not well-defined. 664 | * @param string $group The group value prepended to the $key. 665 | * @return bool Returns TRUE on success or FALSE on failure. 666 | */ 667 | function wp_cache_prepend_by_key( $server_key, $key, $value, $group = '' ) { 668 | return wp_object_cache()->prependByKey( $server_key, $key, $value, $group ); 669 | } 670 | 671 | /** 672 | * Replaces a value in cache. 673 | * 674 | * This method is similar to "add"; however, is does not successfully set a value if 675 | * the object's key is not already set in cache. 676 | * 677 | * @since 2.0.0 678 | * 679 | * @link http://www.php.net/manual/en/memcached.replace.php 680 | * 681 | * @param string $key The key under which to store the value. 682 | * @param mixed $value The value to store. 683 | * @param string $group The group value appended to the $key. 684 | * @param int $expiration The expiration time, defaults to 0. 685 | * @return bool Returns TRUE on success or FALSE on failure. 686 | */ 687 | function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) { 688 | return wp_object_cache()->replace( $key, $value, $group, $expiration ); 689 | } 690 | 691 | /** 692 | * Replaces a value in cache on a specific server. 693 | * 694 | * This method is similar to "addByKey"; however, is does not successfully set a value if 695 | * the object's key is not already set in cache. 696 | * 697 | * @since 2.0.0 698 | * 699 | * @link http://www.php.net/manual/en/memcached.addbykey.php 700 | * 701 | * @param string $server_key The key identifying the server to store the value on. 702 | * @param string $key The key under which to store the value. 703 | * @param mixed $value The value to store. 704 | * @param string $group The group value appended to the $key. 705 | * @param int $expiration The expiration time, defaults to 0. 706 | * @return bool Returns TRUE on success or FALSE on failure. 707 | */ 708 | function wp_cache_replace_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) { 709 | return wp_object_cache()->replaceByKey( $server_key, $key, $value, $group, $expiration ); 710 | } 711 | 712 | /** 713 | * Sets a value in cache. 714 | * 715 | * The value is set whether or not this key already exists in memcached. 716 | * 717 | * @since 2.0.0 718 | * 719 | * @link http://www.php.net/manual/en/memcached.set.php 720 | * 721 | * @param string $key The key under which to store the value. 722 | * @param mixed $value The value to store. 723 | * @param string $group The group value appended to the $key. 724 | * @param int $expiration The expiration time, defaults to 0. 725 | * @return bool Returns TRUE on success or FALSE on failure. 726 | */ 727 | function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) { 728 | return wp_object_cache()->set( $key, $value, $group, $expiration ); 729 | } 730 | 731 | /** 732 | * Sets a value in cache. 733 | * 734 | * The value is set whether or not this key already exists in memcached. 735 | * 736 | * @since 2.0.0 737 | * 738 | * @link http://www.php.net/manual/en/memcached.set.php 739 | * 740 | * @param string $server_key The key identifying the server to store the value on. 741 | * @param string $key The key under which to store the value. 742 | * @param mixed $value The value to store. 743 | * @param string $group The group value appended to the $key. 744 | * @param int $expiration The expiration time, defaults to 0. 745 | * @return bool Returns TRUE on success or FALSE on failure. 746 | */ 747 | function wp_cache_set_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) { 748 | return wp_object_cache()->setByKey( $server_key, $key, $value, $group, $expiration ); 749 | } 750 | 751 | /** 752 | * Set multiple values to cache at once. 753 | * 754 | * By sending an array of $items to this function, all values are saved at once to 755 | * memcached, reducing the need for multiple requests to memcached. The $items array 756 | * keys and values are what are stored to memcached. The keys in the $items array 757 | * are merged with the $groups array/string value via buildKeys to determine the 758 | * final key for the object. 759 | * 760 | * @since 2.0.0 761 | * 762 | * @param array $items An array of key/value pairs to store on the server. 763 | * @param string|array $groups Group(s) to merge with key(s) in $items. 764 | * @param int $expiration The expiration time, defaults to 0. 765 | * @return bool Returns TRUE on success or FALSE on failure. 766 | */ 767 | function wp_cache_set_multi( $items, $groups = '', $expiration = 0 ) { 768 | return wp_object_cache()->setMulti( $items, $groups, $expiration ); 769 | } 770 | 771 | /** 772 | * Set multiple values to cache at once on specified server. 773 | * 774 | * By sending an array of $items to this function, all values are saved at once to 775 | * memcached, reducing the need for multiple requests to memcached. The $items array 776 | * keys and values are what are stored to memcached. The keys in the $items array 777 | * are merged with the $groups array/string value via buildKeys to determine the 778 | * final key for the object. 779 | * 780 | * @since 2.0.0 781 | * 782 | * @param string $server_key The key identifying the server to store the value on. 783 | * @param array $items An array of key/value pairs to store on the server. 784 | * @param string|array $groups Group(s) to merge with key(s) in $items. 785 | * @param int $expiration The expiration time, defaults to 0. 786 | * @return bool Returns TRUE on success or FALSE on failure. 787 | */ 788 | function wp_cache_set_multi_by_key( $server_key, $items, $groups = 'default', $expiration = 0 ) { 789 | return wp_object_cache()->setMultiByKey( $server_key, $items, $groups, $expiration ); 790 | } 791 | 792 | /** 793 | * Set a Memcached option. 794 | * 795 | * @since 2.0.0 796 | * 797 | * @link http://www.php.net/manual/en/memcached.setoption.php 798 | * 799 | * @param int $option Option name. 800 | * @param mixed $value Option value. 801 | * @return bool Returns TRUE on success or FALSE on failure. 802 | */ 803 | function wp_cache_set_option( $option, $value ) { 804 | return wp_object_cache()->setOption( $option, $value ); 805 | } 806 | 807 | /** 808 | * Switch blog prefix, which changes the cache that is accessed. 809 | * 810 | * @since 2.0.0 811 | * 812 | * @param int $blog_id Blog to switch to. 813 | * @return void 814 | */ 815 | function wp_cache_switch_to_blog( $blog_id ) { 816 | return wp_object_cache()->switch_to_blog( $blog_id ); 817 | } 818 | 819 | /** 820 | * Adds a group or set of groups to the list of non-persistent groups. 821 | * 822 | * @since 2.0.0 823 | * 824 | * @param string|array $groups A group or an array of groups to add. 825 | * @return void 826 | */ 827 | function wp_cache_add_global_groups( $groups ) { 828 | wp_object_cache()->add_global_groups( $groups ); 829 | } 830 | 831 | /** 832 | * Adds a group or set of groups to the list of non-persistent groups. 833 | * 834 | * @since 2.0.0 835 | * 836 | * @param string|array $groups A group or an array of groups to add. 837 | * @return void 838 | */ 839 | function wp_cache_add_non_persistent_groups( $groups ) { 840 | wp_object_cache()->add_non_persistent_groups( $groups ); 841 | } 842 | 843 | /** 844 | * Sets up Object Cache Global and assigns it. 845 | * 846 | * @since 2.0.0 847 | * 848 | * @global WP_Spider_Cache_Object $wp_object_cache WordPress Object Cache 849 | * @return void 850 | */ 851 | function wp_cache_init() { 852 | wp_object_cache_init(); 853 | } 854 | 855 | /** 856 | * Returns the Object Cache Global. 857 | * 858 | * @since 2.0.0 859 | * 860 | * @global WP_Spider_Cache_Object $wp_object_cache WordPress Object Cache 861 | * @return WP_Spider_Cache_Object 862 | */ 863 | function wp_object_cache() { 864 | 865 | if ( ! isset( $GLOBALS['wp_object_cache'] ) ) { 866 | wp_object_cache_init(); 867 | } 868 | 869 | return $GLOBALS['wp_object_cache']; 870 | } 871 | 872 | /** 873 | * Sets up Output Cache Global and assigns it. 874 | * 875 | * @since 2.1.0 876 | * 877 | * @global WP_Spider_Cache_Object $wp_object_cache WordPress Object Cache 878 | * @return void 879 | */ 880 | function wp_object_cache_init() { 881 | require_once 'class-object-cache.php'; 882 | $GLOBALS['wp_object_cache'] = new WP_Spider_Cache_Object(); 883 | } 884 | 885 | /** 886 | * Sets up Output Cache Global and assigns it. 887 | * 888 | * @since 2.1.0 889 | * 890 | * @global WP_Spider_Cache_Output $wp_output_cache WordPress Output Cache 891 | * @return void 892 | */ 893 | function wp_output_cache_init() { 894 | require_once 'class-output-cache.php'; 895 | $GLOBALS['wp_output_cache'] = new WP_Spider_Cache_Output(); 896 | } 897 | 898 | /** 899 | * Returns the Output Cache Global. 900 | * 901 | * @since 2.1.0 902 | * 903 | * @global WP_Spider_Cache_Output $wp_output_cache WordPress Output Cache 904 | * @return WP_Spider_Cache_Output 905 | */ 906 | function wp_output_cache() { 907 | 908 | if ( ! isset( $GLOBALS['wp_output_cache'] ) ) { 909 | wp_output_cache_init(); 910 | } 911 | 912 | return $GLOBALS['wp_output_cache']; 913 | } 914 | 915 | /** 916 | * Should we skip the output cache? 917 | * 918 | * @since 2.1.0 919 | * 920 | * @return boolean 921 | */ 922 | function wp_skip_output_cache() { 923 | 924 | // Bail if caching not turned on 925 | if ( ! defined( 'WP_CACHE' ) || ( true !== WP_CACHE ) ) { 926 | return true; 927 | } 928 | 929 | // Bail if no content directory 930 | if ( ! defined( 'WP_CONTENT_DIR' ) ) { 931 | return true; 932 | } 933 | 934 | // Never cache interactive scripts or API endpoints. 935 | if ( in_array( basename( $_SERVER['SCRIPT_FILENAME'] ), array( 936 | 'wp-app.php', 937 | 'wp-cron.php', 938 | 'ms-files.php', 939 | 'xmlrpc.php', 940 | ) ) ) { 941 | return true; 942 | } 943 | 944 | // Never cache JavaScript generators 945 | if ( strstr( $_SERVER['SCRIPT_FILENAME'], 'wp-includes/js' ) ) { 946 | return true; 947 | } 948 | 949 | // Never cache when POST data is present. 950 | if ( ! empty( $GLOBALS['HTTP_RAW_POST_DATA'] ) || ! empty( $_POST ) ) { 951 | return true; 952 | } 953 | 954 | return false; 955 | } 956 | 957 | /** 958 | * Cancel Output-Cache 959 | * 960 | * @since 2.1.0 961 | */ 962 | function wp_output_cache_cancel() { 963 | wp_output_cache()->cancel = true; 964 | } 965 | 966 | /** 967 | * Variants can be set by functions which use early-set globals like $_SERVER to 968 | * run simple tests. Functions defined in WordPress, plugins, and themes are not 969 | * available and MUST NOT be used. 970 | * 971 | * Example: wp_output_cache_vary('return preg_match("/feedburner/i", $_SERVER["HTTP_USER_AGENT"]);'); 972 | * This will cause spider_cache to cache a variant for requests from Feedburner. 973 | * 974 | * Tips for writing $function: 975 | * - DO NOT use any functions from your theme or plugins. Those files have not 976 | * been included. Fatal error. 977 | * - DO NOT use any WordPress functions except from load.php and plugin.php. 978 | * Fatal error. 979 | * - DO NOT include or require files from anywhere without consulting expensive 980 | * professionals first. Fatal error. 981 | * - DO NOT use $wpdb, $blog_id, $current_user, etc. These have not been properly initialized. 982 | * - DO understand how create_function works. This is how your code is used: create_function('', $function); 983 | * - DO remember to return something. The return value determines the cache variant. 984 | * 985 | * @since 2.1.0 986 | */ 987 | function wp_output_cache_vary( $function = '' ) { 988 | 989 | // Bail if no function 990 | if ( empty( $function ) ) { 991 | die( 'Variant determiner cannot be empty.' ); 992 | } 993 | 994 | // Bail if illegal name 995 | if ( preg_match( '/include|require|echo|print|dump|export|open|sock|unlink|`|eval/i', $function ) ) { 996 | die( 'Illegal word in variant determiner.' ); 997 | } 998 | 999 | // Bail if not callable or missing variables 1000 | if ( ! is_callable( $function ) && ! preg_match( '/\$_/', $function ) ) { 1001 | die( 'Variant determiner should be callable or use at least one $_ variable.' ); 1002 | } 1003 | 1004 | wp_output_cache()->add_variant( $function ); 1005 | } 1006 | --------------------------------------------------------------------------------