diff --git a/comet-cache.php b/comet-cache.php index f8a07d6..238641c 100644 --- a/comet-cache.php +++ b/comet-cache.php @@ -1,6 +1,6 @@ =') || ${__FILE__}['apc_enabled'])) { // If PHP <= 5.4 or APC is enabled @@ -22,8 +22,10 @@ } add_action( - 'all_admin_notices', create_function( - '', 'if(!current_user_can(\'activate_plugins\'))'. + 'all_admin_notices', + create_function( + '', + 'if(!current_user_can(\'activate_plugins\'))'. ' return;'."\n".// User missing capability. 'echo \''.// Wrap `$notice` inside a WordPress error. @@ -33,7 +35,7 @@ ''. '\';' - ) + ) ); } elseif (${__FILE__}['apc_enabled'] && is_admin()) { ${__FILE__}['apc_deprecated_notice'] = '

'.__('NOTICE: Comet Cache + PHP APC Extension

', 'comet-cache'); @@ -46,8 +48,10 @@ ${__FILE__}['apc_deprecated_notice'] .= '

'.__('To learn more about this change, please see the announcement: PHP APC Extension No Longer Supported', 'comet-cache').'

'; add_action( - 'all_admin_notices', create_function( - '', 'if(!current_user_can(\'activate_plugins\'))'. + 'all_admin_notices', + create_function( + '', + 'if(!current_user_can(\'activate_plugins\'))'. ' return;'."\n".// User missing capability. 'echo \''.// Wrap `$notice` inside a WordPress error. @@ -57,7 +61,7 @@ ''. '\';' - ) + ) ); } } else { // Load the plugin @@ -68,8 +72,10 @@ ${__FILE__}['mbstring_deprecated_warning'] .= '

'.__('The mbstring extension provides Multibyte String support to PHP and is required to properly handle UTF-8 characters, which many sites now use. Without Multibyte String support, Comet Cache will be unstable. For that reason we are requiring the mbstring extension to improve reliablity when caching and to prevent your site from experiencing unforeseen issues in the future.', 'comet-cache').'

'; ${__FILE__}['mbstring_deprecated_warning'] .= '

'.__('Dismiss this notice.', 'comet-cache').'

'; add_action( - 'all_admin_notices', create_function( - '', 'if(!current_user_can(\'activate_plugins\'))'. + 'all_admin_notices', + create_function( + '', + 'if(!current_user_can(\'activate_plugins\'))'. ' return;'."\n".// User missing capability. 'echo \''.// Wrap `$notice` inside a WordPress error. '
'. @@ -80,7 +86,7 @@ ); } - require_once dirname(__FILE__).'/src/includes/plugin.php'; + require_once __DIR__.'/src/includes/plugin.php'; } } else { wp_php_rv_notice('Comet Cache'); diff --git a/readme.txt b/readme.txt index 242d1d1..11bdb4d 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Comet Cache === -Stable tag: 160227 +Stable tag: 160412-RC Requires at least: 4.1 Tested up to: 4.5-alpha Text Domain: comet-cache diff --git a/src/client-s/css/admin-bar.min.css b/src/client-s/css/admin-bar.min.css index 7332066..c65238f 100644 --- a/src/client-s/css/admin-bar.min.css +++ b/src/client-s/css/admin-bar.min.css @@ -1,2 +1,2 @@  -/*# sourceMappingURL=admin-bar.min.css.map */ + diff --git a/src/client-s/css/menu-pages.min.css b/src/client-s/css/menu-pages.min.css index 6207cc1..3509ba4 100644 --- a/src/client-s/css/menu-pages.min.css +++ b/src/client-s/css/menu-pages.min.css @@ -1,2 +1,2 @@ @font-face{font-family:sharkicons;src:url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.eot?v160221");src:url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.eot?#iefix&v160221") format("embedded-opentype"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.ttf?v160221") format("truetype"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.woff?v160221") format("woff"),url("../../vendor/websharks/sharkicons/src/fonts/sharkicons.svg?v160221#sharkicons") format("svg");font-weight:normal;font-style:normal}.si::before{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none}.si-broom::before{content:""}.si-comment-mail-one::before{content:""}.si-comment-mail::before{content:""}.si-s2member::before{content:""}.si-websharks::before{content:""}.si-wp-kb-articles::before{content:""}.si-zencache-logo::before{content:""}.si-zencache::before{content:""}.si-wp-sharks::before{content:""}.si-wp-sharks-fin::before{content:""}.si-comet-cache::before{content:""}.si-comet-cache-logo::before{content:""}.si-comet-cache-comet::before{content:""}.si-feat-watch::before{content:""}.si-feat-server::before{content:""}.si-feat-layers::before{content:""}.si-feat-box::before{content:""}.si-feat-ellipsis::before{content:""}.si-typi-group::before{content:""}.si-enty-bookmark::before{content:""}.si-enty-bookmarks::before{content:""}.si-enty-open-book::before{content:""}.si-enty-archive::before{content:""}.si-enty-area-graph::before{content:""}.si-enty-bucket::before{content:""}.si-enty-colors::before{content:""}.si-enty-copy::before{content:""}.si-enty-drive::before{content:""}.si-enty-feather::before{content:""}.si-enty-gauge::before{content:""}.si-enty-hand::before{content:""}.si-enty-lab-flask::before{content:""}.si-enty-mask::before{content:""}.si-enty-medal::before{content:""}.si-enty-exclamation::before{content:""}.si-enty-palette::before{content:""}.si-enty-ruler::before{content:""}.si-enty-shop::before{content:""}.si-enty-basket::before{content:""}.si-enty-cart::before{content:""}.si-enty-traffic-cone::before{content:""}.si-enty-tree::before{content:""}.si-enty-trophy::before{content:""}.si-enty-v-card::before{content:""}.si-enty-google-hangouts::before{content:""}.si-eleg-line-graph::before{content:""}.si-eleg-male::before{content:""}.si-eleg-female::before{content:""}.si-eleg-atom::before{content:""}.si-broc-cart::before{content:""}.si-broc-crap::before{content:""}.si-broc-atom::before{content:""}.si-icom-headphones::before{content:""}.si-icom-barcode::before{content:""}.si-icom-user::before{content:""}.si-icom-users::before{content:""}.si-icom-user-plus::before{content:""}.si-icom-user-minus::before{content:""}.si-icom-user-check::before{content:""}.si-icom-user-tie::before{content:""}.si-icom-key::before{content:""}.si-icom-key2::before{content:""}.si-icom-happy::before{content:""}.si-icom-happy2::before{content:""}.si-icom-smile::before{content:""}.si-icom-smile2::before{content:""}.si-icom-tongue::before{content:""}.si-icom-tongue2::before{content:""}.si-icom-sad::before{content:""}.si-icom-sad2::before{content:""}.si-icom-wink::before{content:""}.si-icom-wink2::before{content:""}.si-icom-grin::before{content:""}.si-icom-grin2::before{content:""}.si-icom-cool::before{content:""}.si-icom-cool2::before{content:""}.si-icom-angry::before{content:""}.si-icom-angry2::before{content:""}.si-icom-evil::before{content:""}.si-icom-evil2::before{content:""}.si-icom-shocked::before{content:""}.si-icom-shocked2::before{content:""}.si-icom-baffled::before{content:""}.si-icom-baffled2::before{content:""}.si-icom-confused::before{content:""}.si-icom-confused2::before{content:""}.si-icom-neutral::before{content:""}.si-icom-neutral2::before{content:""}.si-icom-hipster::before{content:""}.si-icom-hipster2::before{content:""}.si-icom-wondering::before{content:""}.si-icom-wondering2::before{content:""}.si-icom-sleepy::before{content:""}.si-icom-sleepy2::before{content:""}.si-icom-frustrated::before{content:""}.si-icom-frustrated2::before{content:""}.si-icom-crying::before{content:""}.si-icom-crying2::before{content:""}.si-icom-spell-check::before{content:""}.si-icom-command-key::before{content:""}.si-icom-shift-key::before{content:""}.si-icom-control-key::before{content:""}.si-icom-option-key::before{content:""}.si-icom-wordpress::before{content:""}.si-icom-wordpress-square::before{content:""}.si-icom-yahoo::before{content:""}.si-icom-linux::before{content:""}.si-icom-finder::before{content:""}.si-icom-android::before{content:""}.si-icom-reddit::before{content:""}.si-icom-paypal::before{content:""}.si-icom-git::before{content:""}.si-octi-alignment-align::before{content:""}.si-octi-alignment-aligned-to::before{content:""}.si-octi-alignment-unalign::before{content:""}.si-octi-bookmark::before{content:""}.si-octi-broadcast::before{content:""}.si-octi-browser::before{content:""}.si-octi-checklist::before{content:""}.si-octi-circuit-board::before{content:""}.si-octi-clippy::before{content:""}.si-octi-cloud-download::before{content:""}.si-octi-cloud-upload::before{content:""}.si-octi-comment::before{content:""}.si-octi-comments::before{content:""}.si-octi-tach::before{content:""}.si-octi-device-camera::before{content:""}.si-octi-device-camera-video::before{content:""}.si-octi-device-desktop::before{content:""}.si-octi-diff::before{content:""}.si-octi-file-binary::before{content:""}.si-octi-file-media::before{content:""}.si-octi-file-submodule::before{content:""}.si-octi-file-symlink-directory::before{content:""}.si-octi-file-symlink-file::before{content:""}.si-octi-fold::before{content:""}.si-octi-git-branch::before{content:""}.si-octi-git-commit::before{content:""}.si-octi-git-compare::before{content:""}.si-octi-git-merge::before{content:""}.si-octi-git-pull-request::before{content:""}.si-octi-graph::before{content:""}.si-octi-home::before{content:""}.si-octi-horizontal-rule::before{content:""}.si-octi-key::before{content:""}.si-octi-light-bulb::before{content:""}.si-octi-link-external::before{content:""}.si-octi-lock::before{content:""}.si-octi-markdown::before{content:""}.si-octi-microscope::before{content:""}.si-octi-mirror::before{content:""}.si-octi-move-down::before{content:""}.si-octi-move-left::before{content:""}.si-octi-move-right::before{content:""}.si-octi-move-up::before{content:""}.si-octi-mute::before{content:""}.si-octi-organization::before{content:""}.si-octi-package::before{content:""}.si-octi-paintcan::before{content:""}.si-octi-person::before{content:""}.si-octi-plug::before{content:""}.si-octi-podium::before{content:""}.si-octi-pulse::before{content:""}.si-octi-puzzle::before{content:""}.si-octi-repo::before{content:""}.si-octi-repo-clone::before{content:""}.si-octi-repo-force-push::before{content:""}.si-octi-repo-forked::before{content:""}.si-octi-repo-pull::before{content:""}.si-octi-repo-push::before{content:""}.si-octi-rocket::before{content:""}.si-octi-ruby::before{content:""}.si-octi-screen-full::before{content:""}.si-octi-screen-normal::before{content:""}.si-octi-sign-in::before{content:""}.si-octi-sign-out::before{content:""}.si-octi-split::before{content:""}.si-octi-squirrel::before{content:""}.si-octi-steps::before{content:""}.si-octi-tag::before{content:""}.si-octi-telescope::before{content:""}.si-octi-terminal::before{content:""}.si-octi-unfold::before{content:""}.si-octi-versions::before{content:""}.si-glass::before{content:""}.si-music::before{content:""}.si-search::before{content:""}.si-envelope-o::before{content:""}.si-heart::before{content:""}.si-star::before{content:""}.si-star-o::before{content:""}.si-user::before{content:""}.si-film::before{content:""}.si-th-large::before{content:""}.si-th::before{content:""}.si-th-list::before{content:""}.si-check::before{content:""}.si-close::before{content:""}.si-search-plus::before{content:""}.si-search-minus::before{content:""}.si-power-off::before{content:""}.si-signal::before{content:""}.si-cog::before{content:""}.si-trash-o::before{content:""}.si-home::before{content:""}.si-file-o::before{content:""}.si-clock-o::before{content:""}.si-road::before{content:""}.si-download::before{content:""}.si-arrow-circle-o-down::before{content:""}.si-arrow-circle-o-up::before{content:""}.si-inbox::before{content:""}.si-play-circle-o::before{content:""}.si-repeat::before{content:""}.si-refresh::before{content:""}.si-list-alt::before{content:""}.si-lock::before{content:""}.si-flag::before{content:""}.si-headphones::before{content:""}.si-volume-off::before{content:""}.si-volume-down::before{content:""}.si-volume-up::before{content:""}.si-qrcode::before{content:""}.si-barcode::before{content:""}.si-tag::before{content:""}.si-tags::before{content:""}.si-book::before{content:""}.si-bookmark::before{content:""}.si-print::before{content:""}.si-camera::before{content:""}.si-font::before{content:""}.si-bold::before{content:""}.si-italic::before{content:""}.si-text-height::before{content:""}.si-text-width::before{content:""}.si-align-left::before{content:""}.si-align-center::before{content:""}.si-align-right::before{content:""}.si-align-justify::before{content:""}.si-list::before{content:""}.si-dedent::before{content:""}.si-indent::before{content:""}.si-video-camera::before{content:""}.si-image::before{content:""}.si-pencil::before{content:""}.si-map-marker::before{content:""}.si-adjust::before{content:""}.si-tint::before{content:""}.si-edit::before{content:""}.si-share-square-o::before{content:""}.si-check-square-o::before{content:""}.si-arrows::before{content:""}.si-step-backward::before{content:""}.si-fast-backward::before{content:""}.si-backward::before{content:""}.si-play::before{content:""}.si-pause::before{content:""}.si-stop::before{content:""}.si-forward::before{content:""}.si-fast-forward::before{content:""}.si-step-forward::before{content:""}.si-eject::before{content:""}.si-chevron-left::before{content:""}.si-chevron-right::before{content:""}.si-plus-circle::before{content:""}.si-minus-circle::before{content:""}.si-times-circle::before{content:""}.si-check-circle::before{content:""}.si-question-circle::before{content:""}.si-info-circle::before{content:""}.si-crosshairs::before{content:""}.si-times-circle-o::before{content:""}.si-check-circle-o::before{content:""}.si-ban::before{content:""}.si-arrow-left::before{content:""}.si-arrow-right::before{content:""}.si-arrow-up::before{content:""}.si-arrow-down::before{content:""}.si-mail-forward::before{content:""}.si-expand::before{content:""}.si-compress::before{content:""}.si-plus::before{content:""}.si-minus::before{content:""}.si-asterisk::before{content:""}.si-exclamation-circle::before{content:""}.si-gift::before{content:""}.si-leaf::before{content:""}.si-fire::before{content:""}.si-eye::before{content:""}.si-eye-slash::before{content:""}.si-exclamation-triangle::before{content:""}.si-plane::before{content:""}.si-calendar::before{content:""}.si-random::before{content:""}.si-comment::before{content:""}.si-magnet::before{content:""}.si-chevron-up::before{content:""}.si-chevron-down::before{content:""}.si-retweet::before{content:""}.si-shopping-cart::before{content:""}.si-folder::before{content:""}.si-folder-open::before{content:""}.si-arrows-v::before{content:""}.si-arrows-h::before{content:""}.si-bar-chart::before{content:""}.si-twitter-square::before{content:""}.si-facebook-square::before{content:""}.si-camera-retro::before{content:""}.si-key::before{content:""}.si-cogs::before{content:""}.si-comments::before{content:""}.si-thumbs-o-up::before{content:""}.si-thumbs-o-down::before{content:""}.si-star-half::before{content:""}.si-heart-o::before{content:""}.si-sign-out::before{content:""}.si-linkedin-square::before{content:""}.si-thumb-tack::before{content:""}.si-external-link::before{content:""}.si-sign-in::before{content:""}.si-trophy::before{content:""}.si-github-square::before{content:""}.si-upload::before{content:""}.si-lemon-o::before{content:""}.si-phone::before{content:""}.si-square-o::before{content:""}.si-bookmark-o::before{content:""}.si-phone-square::before{content:""}.si-twitter::before{content:""}.si-facebook::before{content:""}.si-github::before{content:""}.si-unlock::before{content:""}.si-credit-card::before{content:""}.si-feed::before{content:""}.si-hdd-o::before{content:""}.si-bullhorn::before{content:""}.si-bell-o::before{content:""}.si-certificate::before{content:""}.si-hand-o-right::before{content:""}.si-hand-o-left::before{content:""}.si-hand-o-up::before{content:""}.si-hand-o-down::before{content:""}.si-arrow-circle-left::before{content:""}.si-arrow-circle-right::before{content:""}.si-arrow-circle-up::before{content:""}.si-arrow-circle-down::before{content:""}.si-globe::before{content:""}.si-wrench::before{content:""}.si-tasks::before{content:""}.si-filter::before{content:""}.si-briefcase::before{content:""}.si-arrows-alt::before{content:""}.si-group::before{content:""}.si-chain::before{content:""}.si-cloud::before{content:""}.si-flask::before{content:""}.si-cut::before{content:""}.si-copy::before{content:""}.si-paperclip::before{content:""}.si-floppy-o::before{content:""}.si-square::before{content:""}.si-bars::before{content:""}.si-list-ul::before{content:""}.si-list-ol::before{content:""}.si-strikethrough::before{content:""}.si-underline::before{content:""}.si-table::before{content:""}.si-magic::before{content:""}.si-truck::before{content:""}.si-pinterest::before{content:""}.si-pinterest-square::before{content:""}.si-google-plus-square::before{content:""}.si-google-plus::before{content:""}.si-money::before{content:""}.si-caret-down::before{content:""}.si-caret-up::before{content:""}.si-caret-left::before{content:""}.si-caret-right::before{content:""}.si-columns::before{content:""}.si-sort::before{content:""}.si-sort-desc::before{content:""}.si-sort-asc::before{content:""}.si-envelope::before{content:""}.si-linkedin::before{content:""}.si-rotate-left::before{content:""}.si-gavel::before{content:""}.si-dashboard::before{content:""}.si-comment-o::before{content:""}.si-comments-o::before{content:""}.si-bolt::before{content:""}.si-sitemap::before{content:""}.si-umbrella::before{content:""}.si-clipboard::before{content:""}.si-lightbulb-o::before{content:""}.si-exchange::before{content:""}.si-cloud-download::before{content:""}.si-cloud-upload::before{content:""}.si-user-md::before{content:""}.si-stethoscope::before{content:""}.si-suitcase::before{content:""}.si-bell::before{content:""}.si-coffee::before{content:""}.si-cutlery::before{content:""}.si-file-text-o::before{content:""}.si-building-o::before{content:""}.si-hospital-o::before{content:""}.si-ambulance::before{content:""}.si-medkit::before{content:""}.si-fighter-jet::before{content:""}.si-beer::before{content:""}.si-h-square::before{content:""}.si-plus-square::before{content:""}.si-angle-double-left::before{content:""}.si-angle-double-right::before{content:""}.si-angle-double-up::before{content:""}.si-angle-double-down::before{content:""}.si-angle-left::before{content:""}.si-angle-right::before{content:""}.si-angle-up::before{content:""}.si-angle-down::before{content:""}.si-desktop::before{content:""}.si-laptop::before{content:""}.si-tablet::before{content:""}.si-mobile::before{content:""}.si-circle-o::before{content:""}.si-quote-left::before{content:""}.si-quote-right::before{content:""}.si-spinner::before{content:""}.si-circle::before{content:""}.si-mail-reply::before{content:""}.si-github-alt::before{content:""}.si-folder-o::before{content:""}.si-folder-open-o::before{content:""}.si-smile-o::before{content:""}.si-frown-o::before{content:""}.si-meh-o::before{content:""}.si-gamepad::before{content:""}.si-keyboard-o::before{content:""}.si-flag-o::before{content:""}.si-flag-checkered::before{content:""}.si-terminal::before{content:""}.si-code::before{content:""}.si-mail-reply-all::before{content:""}.si-star-half-empty::before{content:""}.si-location-arrow::before{content:""}.si-crop::before{content:""}.si-code-fork::before{content:""}.si-chain-broken::before{content:""}.si-question::before{content:""}.si-info::before{content:""}.si-exclamation::before{content:""}.si-superscript::before{content:""}.si-subscript::before{content:""}.si-eraser::before{content:""}.si-puzzle-piece::before{content:""}.si-microphone::before{content:""}.si-microphone-slash::before{content:""}.si-shield::before{content:""}.si-calendar-o::before{content:""}.si-fire-extinguisher::before{content:""}.si-rocket::before{content:""}.si-maxcdn::before{content:""}.si-chevron-circle-left::before{content:""}.si-chevron-circle-right::before{content:""}.si-chevron-circle-up::before{content:""}.si-chevron-circle-down::before{content:""}.si-html5::before{content:""}.si-css3::before{content:""}.si-anchor::before{content:""}.si-unlock-alt::before{content:""}.si-bullseye::before{content:""}.si-ellipsis-h::before{content:""}.si-ellipsis-v::before{content:""}.si-rss-square::before{content:""}.si-play-circle::before{content:""}.si-ticket::before{content:""}.si-minus-square::before{content:""}.si-minus-square-o::before{content:""}.si-level-up::before{content:""}.si-level-down::before{content:""}.si-check-square::before{content:""}.si-pencil-square::before{content:""}.si-external-link-square::before{content:""}.si-share-square::before{content:""}.si-compass::before{content:""}.si-caret-square-o-down::before{content:""}.si-caret-square-o-up::before{content:""}.si-caret-square-o-right::before{content:""}.si-eur::before{content:""}.si-gbp::before{content:""}.si-dollar::before{content:""}.si-inr::before{content:""}.si-cny::before{content:""}.si-rouble::before{content:""}.si-krw::before{content:""}.si-bitcoin::before{content:""}.si-file::before{content:""}.si-file-text::before{content:""}.si-sort-alpha-asc::before{content:""}.si-sort-alpha-desc::before{content:""}.si-sort-amount-asc::before{content:""}.si-sort-amount-desc::before{content:""}.si-sort-numeric-asc::before{content:""}.si-sort-numeric-desc::before{content:""}.si-thumbs-up::before{content:""}.si-thumbs-down::before{content:""}.si-youtube-square::before{content:""}.si-youtube::before{content:""}.si-xing::before{content:""}.si-xing-square::before{content:""}.si-youtube-play::before{content:""}.si-dropbox::before{content:""}.si-stack-overflow::before{content:""}.si-instagram::before{content:""}.si-flickr::before{content:""}.si-adn::before{content:""}.si-bitbucket::before{content:""}.si-bitbucket-square::before{content:""}.si-tumblr::before{content:""}.si-tumblr-square::before{content:""}.si-long-arrow-down::before{content:""}.si-long-arrow-up::before{content:""}.si-long-arrow-left::before{content:""}.si-long-arrow-right::before{content:""}.si-apple::before{content:""}.si-windows::before{content:""}.si-android::before{content:""}.si-linux::before{content:""}.si-dribbble::before{content:""}.si-skype::before{content:""}.si-foursquare::before{content:""}.si-trello::before{content:""}.si-female::before{content:""}.si-male::before{content:""}.si-gittip::before{content:""}.si-sun-o::before{content:""}.si-moon-o::before{content:""}.si-archive::before{content:""}.si-bug::before{content:""}.si-vk::before{content:""}.si-weibo::before{content:""}.si-renren::before{content:""}.si-pagelines::before{content:""}.si-stack-exchange::before{content:""}.si-arrow-circle-o-right::before{content:""}.si-arrow-circle-o-left::before{content:""}.si-caret-square-o-left::before{content:""}.si-dot-circle-o::before{content:""}.si-wheelchair::before{content:""}.si-vimeo-square::before{content:""}.si-try::before{content:""}.si-plus-square-o::before{content:""}.si-space-shuttle::before{content:""}.si-slack::before{content:""}.si-envelope-square::before{content:""}.si-wordpress::before{content:""}.si-openid::before{content:""}.si-bank::before{content:""}.si-graduation-cap::before{content:""}.si-yahoo::before{content:""}.si-google::before{content:""}.si-reddit::before{content:""}.si-reddit-square::before{content:""}.si-stumbleupon-circle::before{content:""}.si-stumbleupon::before{content:""}.si-delicious::before{content:""}.si-digg::before{content:""}.si-pied-piper::before{content:""}.si-pied-piper-alt::before{content:""}.si-drupal::before{content:""}.si-joomla::before{content:""}.si-language::before{content:""}.si-fax::before{content:""}.si-building::before{content:""}.si-child::before{content:""}.si-paw::before{content:""}.si-spoon::before{content:""}.si-cube::before{content:""}.si-cubes::before{content:""}.si-behance::before{content:""}.si-behance-square::before{content:""}.si-steam::before{content:""}.si-steam-square::before{content:""}.si-recycle::before{content:""}.si-automobile::before{content:""}.si-cab::before{content:""}.si-tree::before{content:""}.si-spotify::before{content:""}.si-deviantart::before{content:""}.si-soundcloud::before{content:""}.si-database::before{content:""}.si-file-pdf-o::before{content:""}.si-file-word-o::before{content:""}.si-file-excel-o::before{content:""}.si-file-powerpoint-o::before{content:""}.si-file-image-o::before{content:""}.si-file-archive-o::before{content:""}.si-file-audio-o::before{content:""}.si-file-movie-o::before{content:""}.si-file-code-o::before{content:""}.si-vine::before{content:""}.si-codepen::before{content:""}.si-jsfiddle::before{content:""}.si-life-bouy::before{content:""}.si-circle-o-notch::before{content:""}.si-ra::before{content:""}.si-empire::before{content:""}.si-git-square::before{content:""}.si-git::before{content:""}.si-hacker-news::before{content:""}.si-tencent-weibo::before{content:""}.si-qq::before{content:""}.si-wechat::before{content:""}.si-paper-plane::before{content:""}.si-paper-plane-o::before{content:""}.si-history::before{content:""}.si-circle-thin::before{content:""}.si-header::before{content:""}.si-paragraph::before{content:""}.si-sliders::before{content:""}.si-share-alt::before{content:""}.si-share-alt-square::before{content:""}.si-bomb::before{content:""}.si-futbol-o::before{content:""}.si-tty::before{content:""}.si-binoculars::before{content:""}.si-plug::before{content:""}.si-slideshare::before{content:""}.si-twitch::before{content:""}.si-yelp::before{content:""}.si-newspaper-o::before{content:""}.si-wifi::before{content:""}.si-calculator::before{content:""}.si-paypal::before{content:""}.si-google-wallet::before{content:""}.si-cc-visa::before{content:""}.si-cc-mastercard::before{content:""}.si-cc-discover::before{content:""}.si-cc-amex::before{content:""}.si-cc-paypal::before{content:""}.si-cc-stripe::before{content:""}.si-bell-slash::before{content:""}.si-bell-slash-o::before{content:""}.si-trash::before{content:""}.si-copyright::before{content:""}.si-at::before{content:""}.si-eyedropper::before{content:""}.si-paint-brush::before{content:""}.si-birthday-cake::before{content:""}.si-area-chart::before{content:""}.si-pie-chart::before{content:""}.si-line-chart::before{content:""}.si-lastfm::before{content:""}.si-lastfm-square::before{content:""}.si-toggle-off::before{content:""}.si-toggle-on::before{content:""}.si-bicycle::before{content:""}.si-bus::before{content:""}.si-ioxhost::before{content:""}.si-angellist::before{content:""}.si-cc::before{content:""}.si-ils::before{content:""}.si-meanpath::before{content:""}.si-buysellads::before{content:""}.si-connectdevelop::before{content:""}.si-dashcube::before{content:""}.si-forumbee::before{content:""}.si-leanpub::before{content:""}.si-sellsy::before{content:""}.si-shirtsinbulk::before{content:""}.si-simplybuilt::before{content:""}.si-skyatlas::before{content:""}.si-cart-plus::before{content:""}.si-cart-arrow-down::before{content:""}.si-diamond::before{content:""}.si-ship::before{content:""}.si-user-secret::before{content:""}.si-motorcycle::before{content:""}.si-street-view::before{content:""}.si-heartbeat::before{content:""}.si-venus::before{content:""}.si-mars::before{content:""}.si-mercury::before{content:""}.si-intersex::before{content:""}.si-transgender-alt::before{content:""}.si-venus-double::before{content:""}.si-mars-double::before{content:""}.si-venus-mars::before{content:""}.si-mars-stroke::before{content:""}.si-mars-stroke-v::before{content:""}.si-mars-stroke-h::before{content:""}.si-neuter::before{content:""}.si-genderless::before{content:""}.si-facebook-official::before{content:""}.si-pinterest-p::before{content:""}.si-whatsapp::before{content:""}.si-server::before{content:""}.si-user-plus::before{content:""}.si-user-times::before{content:""}.si-bed::before{content:""}.si-viacoin::before{content:""}.si-train::before{content:""}.si-subway::before{content:""}.si-medium::before{content:""}.si-y-combinator::before{content:""}.si-optin-monster::before{content:""}.si-opencart::before{content:""}.si-expeditedssl::before{content:""}.si-battery-4::before{content:""}.si-battery-3::before{content:""}.si-battery-2::before{content:""}.si-battery-1::before{content:""}.si-battery-0::before{content:""}.si-mouse-pointer::before{content:""}.si-i-cursor::before{content:""}.si-object-group::before{content:""}.si-object-ungroup::before{content:""}.si-sticky-note::before{content:""}.si-sticky-note-o::before{content:""}.si-cc-jcb::before{content:""}.si-cc-diners-club::before{content:""}.si-clone::before{content:""}.si-balance-scale::before{content:""}.si-hourglass-o::before{content:""}.si-hourglass-1::before{content:""}.si-hourglass-2::before{content:""}.si-hourglass-3::before{content:""}.si-hourglass::before{content:""}.si-hand-grab-o::before{content:""}.si-hand-paper-o::before{content:""}.si-hand-scissors-o::before{content:""}.si-hand-lizard-o::before{content:""}.si-hand-spock-o::before{content:""}.si-hand-pointer-o::before{content:""}.si-hand-peace-o::before{content:""}.si-trademark::before{content:""}.si-registered::before{content:""}.si-creative-commons::before{content:""}.si-gg::before{content:""}.si-gg-circle::before{content:""}.si-tripadvisor::before{content:""}.si-odnoklassniki::before{content:""}.si-odnoklassniki-square::before{content:""}.si-get-pocket::before{content:""}.si-wikipedia-w::before{content:""}.si-safari::before{content:""}.si-chrome::before{content:""}.si-firefox::before{content:""}.si-opera::before{content:""}.si-internet-explorer::before{content:""}.si-television::before{content:""}.si-contao::before{content:""}.si-500px::before{content:""}.si-amazon::before{content:""}.si-calendar-plus-o::before{content:""}.si-calendar-minus-o::before{content:""}.si-calendar-times-o::before{content:""}.si-calendar-check-o::before{content:""}.si-industry::before{content:""}.si-map-pin::before{content:""}.si-map-signs::before{content:""}.si-map-o::before{content:""}.si-map::before{content:""}.si-commenting::before{content:""}.si-commenting-o::before{content:""}.si-houzz::before{content:""}.si-vimeo::before{content:""}.si-black-tie::before{content:""}.si-fonticons::before{content:""}.si-lg{font-size:1.33333333em;line-height:0.75em;vertical-align:-15%}.si-2x{font-size:2em}.si-3x{font-size:3em}.si-4x{font-size:4em}.si-5x{font-size:5em}.si-fw{text-align:center;width:1.28571429em}.si-border{border-radius:.1em;padding:.2em .25em .15em;border:solid 0.08em}.si-pull-left{float:left;margin-right:.3em}.si-pull-right{float:right;margin-left:.3em}.si-rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.si-rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.si-rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.si-flip-horizontal{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.si-flip-vertical{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.si-inverse{-webkit-filter:invert(100%);filter:invert(100%)}.si-spin{-webkit-animation:si-animation-spin 2s infinite linear;-moz-animation:si-animation-spin 2s infinite linear;animation:si-animation-spin 2s infinite linear}.si-pulse{-webkit-animation:si-animation-spin 1s infinite steps(8);-moz-animation:si-animation-spin 1s infinite steps(8);animation:si-animation-spin 1s infinite steps(8)}.si-ul{padding-left:0;list-style-type:none;margin-left:2.14285714em}.si-ul>li{position:relative}.si-ul>li .si-li{text-align:center;position:absolute;left:-2.14285714em;width:2.14285714em;top:0.14285714em}.si-ul>li .si-li .si-lg{left:-1.85714286em}.si-stack{width:2em;height:2em;line-height:2em;vertical-align:middle;position:relative;display:inline-block}.si-stack .si-stack-1x,.si-stack .si-stack-2x{left:0;width:100%;text-align:center;position:absolute}.si-stack .si-stack-1x{line-height:inherit}.si-stack .si-stack-2x{font-size:2em}@-webkit-keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-moz-keyframes si-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}}.plugin-menu-page-animation-spin{-webkit-animation-duration:0.75s;-moz-animation-duration:0.75s;animation-duration:0.75s;-webkit-animation-fill-mode:both;-moz-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;-moz-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:plugin-menu-page-animation-spin;-moz-animation-name:plugin-menu-page-animation-spin;animation-name:plugin-menu-page-animation-spin}@-webkit-keyframes plugin-menu-page-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@-moz-keyframes plugin-menu-page-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(360deg)}}@keyframes plugin-menu-page-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}html{overflow-y:scroll}#wpwrap{position:relative}#wpwrap::after{top:0;right:0;bottom:0;left:0;z-index:-1;position:absolute;content:'';opacity:0.25;background:url("../images/bg.png")}#wpwrap .updated,#wpwrap .error{margin:1.25em 1.25em 1.25em 0.25em}.plugin-menu-page{min-width:800px;margin:1.25em 1.25em 1.25em 0.25em}.plugin-menu-page,.plugin-menu-page p{font-size:14px}.plugin-menu-page a{color:#033A63}.plugin-menu-page a:hover,.plugin-menu-page a:active{color:#467629}.plugin-menu-page p:first-child,.plugin-menu-page pre:first-child{margin-top:0}.plugin-menu-page p:last-child,.plugin-menu-page pre:last-child{margin-bottom:0}.plugin-menu-page code{border-radius:3px;padding:0.1em 0.25em;background:rgba(178,178,178,0.25);font-family:'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace}.plugin-menu-page pre.code{padding:0;background:none}.plugin-menu-page pre.code>code{font-size:90%;overflow-x:auto;max-width:100%;border-radius:4px;padding:1em;display:block;color:#eee;background:#222;box-shadow:0 0 5px 1px #000 inset}.plugin-menu-page img{border:0}.plugin-menu-page img.screenshot{float:right;border-radius:4px;padding:0.5em;margin:0 0 2em 2em;background:#fff;border:1px solid #afafaf;box-shadow:0 0 5px 0 rgba(0,0,0,0.2) inset}.plugin-menu-page hr{border:0;padding:0;height:1px;margin:1em 0;background:linear-gradient(to left, transparent, rgba(0,0,0,0.75), transparent)}.plugin-menu-page label{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.plugin-menu-page label.switch-primary{font-size:130%;margin:0;padding:0.5em;border-radius:4px;display:inline;color:#000;background:#f1e982;border:1px solid rgba(0,0,0,0.07);box-shadow:-1px -1px 0 0 rgba(0,0,0,0.25) inset,1px 1px 0 0 #fff inset}.plugin-menu-page select,.plugin-menu-page textarea,.plugin-menu-page select:focus,.plugin-menu-page textarea:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']),.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{width:100%;line-height:1.3em;margin:0;padding:0.25em 0.5em;border-radius:4px;box-sizing:border-box;color:#333;background:#e8e8e8;border:1px solid #848484;box-shadow:0 0 2px 0 rgba(132,132,132,0.5) inset}.plugin-menu-page select,.plugin-menu-page select:focus{box-shadow:0 1px 0 0 #fff inset,0 -2px 3px 0 rgba(132,132,132,0.25) inset}.plugin-menu-page select,.plugin-menu-page select:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']),.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{height:2em}.plugin-menu-page select:focus,.plugin-menu-page textarea:focus,.plugin-menu-page input:not([type='radio']):not([type='checkbox']):focus{color:#000;background:#e2e2e2}.plugin-menu-page input[disabled],.plugin-menu-page select[disabled],.plugin-menu-page textarea[disabled]{opacity:0.5}.plugin-menu-page input::-webkit-input-placeholder,.plugin-menu-page textarea::-webkit-input-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input::-moz-placeholder,.plugin-menu-page textarea::-moz-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input:-moz-placeholder,.plugin-menu-page textarea:-moz-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page input:-ms-input-placeholder,.plugin-menu-page textarea:-ms-input-placeholder{font-style:italic;color:rgba(0,0,0,0.2)}.plugin-menu-page table{margin:1em 0}.plugin-menu-page button{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:150%;font-weight:bold;line-height:1em;outline:none;cursor:pointer;border-radius:4px;margin:0;padding:0.25em 0.5em;box-sizing:border-box;color:#fff;background:#033A63;border:1px solid rgba(0,0,0,0.5);box-shadow:0 1px 1px 0 rgba(0,0,0,0.25),-1px -1px 0 0 rgba(0,0,0,0.2) inset,1px 1px 0 0 rgba(255,255,255,0.1) inset}.plugin-menu-page button:hover{background:#467629}.plugin-menu-page button[type='submit']{background:#467629}.plugin-menu-page button[type='submit']:hover{background:#033A63}.plugin-menu-page button:active{-webkit-transform:scale(0.98, 0.98);-moz-transform:scale(0.98, 0.98);-ms-transform:scale(0.98, 0.98);-o-transform:scale(0.98, 0.98);transform:scale(0.98, 0.98)}.plugin-menu-page .info,.plugin-menu-page .notice,.plugin-menu-page .warning,.plugin-menu-page .error{border-radius:4px;padding:0.5em;margin:1em 0}.plugin-menu-page .info{background:#cadfed;border:1px solid #216095}.plugin-menu-page .notice{background:#fffde8;border:1px solid #e6db55}.plugin-menu-page .warning{background:#ffefd3;border:1px solid #e6db55}.plugin-menu-page .error{background:pink;border:1px solid #711e1e}.plugin-menu-page .monospace{font-family:'Menlo', 'Monaco', 'Consolas', 'Courier New', monospace}.plugin-menu-page textarea.monospace{white-space:pre}.plugin-menu-page .clearfix::before,.plugin-menu-page .clearfix::after{display:table;content:' '}.plugin-menu-page .clearfix::after{clear:both}.plugin-menu-page-heading .plugin-menu-page-restore-defaults,.plugin-menu-page-heading .plugin-menu-page-panel-togglers{float:right;margin:0 1em 0 0}.plugin-menu-page-heading .plugin-menu-page-panel-togglers button{background:#033A63 !important}.plugin-menu-page-heading .plugin-menu-page-upsells{float:right;clear:right;text-align:right;max-width:350px;margin:1em 0 0}.plugin-menu-page-heading .plugin-menu-page-upsells a{text-decoration:none;line-height:1.5em;margin:0 0.5em;display:inline-block}.plugin-menu-page-heading .plugin-menu-page-support-links,.plugin-menu-page-heading .plugin-menu-page-mailing-list-links{float:right;clear:right;text-align:right;max-width:400px;margin:.5em 0 0}.plugin-menu-page-heading .plugin-menu-page-support-links a,.plugin-menu-page-heading .plugin-menu-page-mailing-list-links a{text-decoration:none;margin:0 0.5em;display:inline-block}.plugin-menu-page-heading .plugin-menu-page-version{float:right;clear:right;min-width:350px;margin:0.5em 0 0 0;text-align:right}.plugin-menu-page-body{position:relative}.plugin-menu-page-section-heading{margin:1em 0}.plugin-menu-page-section-heading>small{font-size:65%;font-style:italic;margin:0;display:block;opacity:0.5}.plugin-menu-page-panel{margin:1em 0}.plugin-menu-page-panel:first-child{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-heading{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;padding:10px;font-size:150%;line-height:1.125em;font-weight:bold;border-radius:4px;display:block;cursor:pointer;background:#033A63;background:linear-gradient(to right, #033A63, #20669A);color:#eee !important;box-shadow:0 2px 2px 0 rgba(0,0,0,0.25)}.plugin-menu-page-panel .plugin-menu-page-panel-heading::after{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none;content:""}.plugin-menu-page-panel .plugin-menu-page-panel-heading:hover,.plugin-menu-page-panel .plugin-menu-page-panel-heading.open{color:#fff !important}.plugin-menu-page-panel .plugin-menu-page-panel-heading::after{font-size:80%;float:right;margin:0 0 0 5px}.plugin-menu-page-panel .plugin-menu-page-panel-heading.open::after{content:""}.plugin-menu-page-panel-heading.pro-preview-feature{background:#216095}.pro-preview-feature::after{font-variant:small-caps !important;font-family:sans-serif !important;content:'pro version only' !important;margin-left:15px;background:#216095;color:#FFFFFF;padding:0 5px 2px 5px;font-weight:normal}.plugin-menu-page-panel-heading.pro-preview-additional-features{background:#216095}.pro-preview-additional-features::after{font-variant:small-caps !important;font-family:sans-serif !important;content:'additional pro version only features' !important;margin-left:15px;background:#216095;color:#FFFFFF;padding:0 5px 2px 5px;font-weight:normal}.plugin-menu-page-panel .plugin-menu-page-panel-heading>.si{text-align:center;width:1.28571429em;margin-right:.25em}.plugin-menu-page-panel .plugin-menu-page-panel-body{width:99%;margin:0 auto;display:none;padding:1.2em;border-bottom-left-radius:4px;border-bottom-right-radius:4px;box-sizing:border-box;color:#222;border:1px solid #848484;background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,0.25),0 3px 1px -1px rgba(0,0,0,0.25) inset}.plugin-menu-page-panel .plugin-menu-page-panel-body.open{display:block}.plugin-menu-page-panel .plugin-menu-page-panel-body p{font-size:90%;color:#666}.plugin-menu-page-panel .plugin-menu-page-panel-body p.notice,.plugin-menu-page-panel .plugin-menu-page-panel-body p.info,.plugin-menu-page-panel .plugin-menu-page-panel-body p.warning,.plugin-menu-page-panel .plugin-menu-page-panel-body p.error{color:#000}.plugin-menu-page-panel .plugin-menu-page-panel-body h3{margin:0 0 0.5em}.plugin-menu-page-panel .plugin-menu-page-panel-body h3:first-child{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-body h3+p{margin-top:0}.plugin-menu-page-panel .plugin-menu-page-panel-body a.dotted{text-decoration:none;border-bottom:1px dotted}.plugin-menu-page-panel .plugin-menu-page-panel-body.pro-preview,.plugin-menu-page-panel .plugin-menu-page-panel-body .pro-preview{opacity:0.5}.plugin-menu-page-save{margin-top:2em}.plugin-menu-page-save button{line-height:1.3em;width:100%} -/*# sourceMappingURL=menu-pages.min.css.map */ + diff --git a/src/includes/api.php b/src/includes/api.php index fd588ce..c0ec55f 100644 --- a/src/includes/api.php +++ b/src/includes/api.php @@ -6,11 +6,13 @@ */ namespace WebSharks\CometCache; +use WebSharks\CometCache\Classes; + if (!defined('WPINC')) { exit('Do NOT access this file directly: '.basename(__FILE__)); } -class_alias(__NAMESPACE__.'\\ApiBase', GLOBAL_NS); +class_alias(__NAMESPACE__.'\\Classes\\ApiBase', GLOBAL_NS); if (!class_exists('zencache')) { - class_alias(__NAMESPACE__.'\\ApiBase', 'zencache'); + class_alias(__NAMESPACE__.'\\Classes\\ApiBase', 'zencache'); } diff --git a/src/includes/classes/AbsBase.php b/src/includes/classes/AbsBase.php index 62d03c5..1bf82f7 100644 --- a/src/includes/classes/AbsBase.php +++ b/src/includes/classes/AbsBase.php @@ -1,5 +1,5 @@ static = &static::$global_static[$class]; @@ -76,7 +76,8 @@ public static function instance() if (isset($instance)) { return $instance; } - return ($instance = new static()); + + return $instance = new static(); } /** @@ -167,7 +168,7 @@ public function __unset($property) * @note This function returns by reference. The use of `&` is highly recommended when calling this utility. * See also: */ - public function &cacheKey($function, $args = array(), $___prop = 'cache') + public function &cacheKey($function, $args = [], $___prop = 'cache') { $function = (string) $function; $args = (array) $args; @@ -210,6 +211,7 @@ public function &cacheKey($function, $args = array(), $___prop = 'cache') } $cache_key = &$cache_key[$_key]; } + return $cache_key; } @@ -224,7 +226,7 @@ public function &cacheKey($function, $args = array(), $___prop = 'cache') * @note This function returns by reference. The use of `&` is highly recommended when calling this utility. * See also: */ - public function &staticKey($function, $args = array()) + public function &staticKey($function, $args = []) { $key = &$this->cacheKey($function, $args, 'static'); diff --git a/src/includes/classes/AbsBaseAp.php b/src/includes/classes/AbsBaseAp.php index 6505b81..6231e7d 100644 --- a/src/includes/classes/AbsBaseAp.php +++ b/src/includes/classes/AbsBaseAp.php @@ -1,13 +1,39 @@ GLOBAL_NS, GLOBAL_NS.'_cache_wiped' => '1'); + $query_args = ['page' => GLOBAL_NS, GLOBAL_NS.'_cache_wiped' => '1']; $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to); wp_redirect($redirect_to).exit(); @@ -107,7 +107,7 @@ protected function clearCache($args) $redirect_to = self_admin_url('/admin.php'); // Redirect preparations. - $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_cache_cleared' => '1'); + $query_args = ['page' => GLOBAL_NS, GLOBAL_NS.'_cache_cleared' => '1']; $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to); wp_redirect($redirect_to).exit(); @@ -167,7 +167,7 @@ protected function saveOptions($args) delete_transient(GLOBAL_NS.'-'.md5($this->plugin->options['auto_cache_sitemap_url'])); $redirect_to = self_admin_url('/admin.php'); // Redirect preparations. - $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_updated' => '1'); + $query_args = ['page' => GLOBAL_NS, GLOBAL_NS.'_updated' => '1']; $this->plugin->autoWipeCache(); // May produce a notice. @@ -232,7 +232,7 @@ protected function restoreDefaultOptions($args) $this->plugin->restoreDefaultOptions(); // Restore defaults. $redirect_to = self_admin_url('/admin.php'); // Redirect preparations. - $query_args = array('page' => GLOBAL_NS, GLOBAL_NS.'_restored' => '1'); + $query_args = ['page' => GLOBAL_NS, GLOBAL_NS.'_restored' => '1']; $this->plugin->autoWipeCache(); // May produce a notice. diff --git a/src/includes/classes/AdvCacheBackCompat.php b/src/includes/classes/AdvCacheBackCompat.php index 69ed55f..45fd1f1 100644 --- a/src/includes/classes/AdvCacheBackCompat.php +++ b/src/includes/classes/AdvCacheBackCompat.php @@ -1,5 +1,5 @@ &$_GET, '_REQUEST' => &$_REQUEST, - ); - $zc_suffixes = array('AC', 'ABC'); + ]; + $zc_suffixes = ['AC', 'ABC']; foreach ($super_gs as $_super_g_key => &$_super_g_value) { foreach ($zc_suffixes as $_zc_suffix) { diff --git a/src/includes/classes/AdvancedCache.php b/src/includes/classes/AdvancedCache.php index 013b327..16c6636 100644 --- a/src/includes/classes/AdvancedCache.php +++ b/src/includes/classes/AdvancedCache.php @@ -1,5 +1,7 @@ buildCachePathRegexFromWcUrl($url); + return $GLOBALS[GLOBAL_NS]->deleteFilesFromCacheDir($regex); } diff --git a/src/includes/classes/Conflicts.php b/src/includes/classes/Conflicts.php index 5b873fe..7e0b6a0 100644 --- a/src/includes/classes/Conflicts.php +++ b/src/includes/classes/Conflicts.php @@ -1,5 +1,5 @@ home_url = rtrim(home_url(), '/'); $this->default_feed = get_default_feed(); // Default feed type. $this->seo_friendly_permalinks = (boolean) get_option('permalink_structure'); - $this->feed_types = array_unique(array($this->default_feed, 'rdf', 'rss', 'rss2', 'atom')); + $this->feed_types = array_unique([$this->default_feed, 'rdf', 'rss', 'rss2', 'atom']); } /** @@ -62,7 +62,7 @@ public function __construct() */ public function feedLinkVariations($type_prefix = '') { - $variations = array(); // Initialize. + $variations = []; // Initialize. foreach ($this->feed_types as $_feed_type) { $variations[] = get_feed_link((string) $type_prefix.$_feed_type); @@ -83,7 +83,7 @@ public function feedLinkVariations($type_prefix = '') */ public function postCommentsFeedLinkVariations(\WP_Post $post) { - $variations = array(); // Initialize. + $variations = []; // Initialize. foreach ($this->feed_types as $_feed_type) { $variations[] = get_post_comments_feed_link($post->ID, $_feed_type); @@ -104,15 +104,15 @@ public function postCommentsFeedLinkVariations(\WP_Post $post) */ public function postAuthorFeedLinkVariations(\WP_Post $post) { - $variations = array(); // Initialize. + $variations = []; // Initialize. foreach ($this->feed_types as $_feed_type) { $variations[] = get_author_feed_link($post->post_author, $_feed_type); } if ($this->seo_friendly_permalinks && ($post_author = get_userdata($post->post_author))) { foreach ($this->feed_types as $_feed_type) { - $variations[] = add_query_arg(urlencode_deep(array('author' => $post->post_author)), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); - $variations[] = add_query_arg(urlencode_deep(array('author' => $post_author->user_nicename)), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); + $variations[] = add_query_arg(urlencode_deep(['author' => $post->post_author]), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); + $variations[] = add_query_arg(urlencode_deep(['author' => $post_author->user_nicename]), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); } } unset($_feed_type); // Housekeeping. @@ -131,7 +131,7 @@ public function postAuthorFeedLinkVariations(\WP_Post $post) */ public function postTypeArchiveFeedLinkVariations(\WP_Post $post) { - $variations = array(); // Initialize. + $variations = []; // Initialize. foreach ($this->feed_types as $_feed_type) { $variations[] = get_post_type_archive_feed_link($post->post_type, $_feed_type); @@ -158,7 +158,7 @@ public function postTypeArchiveFeedLinkVariations(\WP_Post $post) */ public function postTermFeedLinkVariations(\WP_Post $post, $include_regex_wildcard_keys = false) { - $variations = $post_terms = array(); // Initialize. + $variations = $post_terms = []; // Initialize. if (!is_array($post_taxonomies = get_object_taxonomies($post, 'objects')) || !$post_taxonomies) { return $variations; // Nothing to do here; post has no terms. @@ -202,10 +202,10 @@ public function postTermFeedLinkVariations(\WP_Post $post, $include_regex_wildca $_taxonomy_query_var = $_taxonomy->query_var; } foreach ($this->feed_types as $_feed_type) { - $variations[] = add_query_arg(urlencode_deep(array($_taxonomy_query_var => $_post_term->term_id)), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); + $variations[] = add_query_arg(urlencode_deep([$_taxonomy_query_var => $_post_term->term_id]), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); } foreach ($this->feed_types as $_feed_type) { - $variations[] = add_query_arg(urlencode_deep(array($_taxonomy_query_var => $_post_term->slug)), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); + $variations[] = add_query_arg(urlencode_deep([$_taxonomy_query_var => $_post_term->slug]), $this->home_url.'/feed/'.urlencode($_feed_type).'/'); } } unset($_taxonomy, $_taxonomy_query_var, $_feed_type); // Housekeeping. @@ -226,11 +226,11 @@ public function postTermFeedLinkVariations(\WP_Post $post, $include_regex_wildca */ public function convertVariationsToHostCachePathRegexFrags(array $variations) { - $regex_frags = array(); + $regex_frags = []; $is_multisite = is_multisite(); $can_consider_domain_mapping = $is_multisite && $this->plugin->canConsiderDomainMapping(); - $flags = CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST // Default flags. - | CACHE_PATH_NO_USER | CACHE_PATH_NO_VSALT | CACHE_PATH_NO_EXT; + $flags = $this::CACHE_PATH_NO_SCHEME | $this::CACHE_PATH_NO_HOST // Default flags. + | $this::CACHE_PATH_NO_USER | $this::CACHE_PATH_NO_VSALT | $this::CACHE_PATH_NO_EXT; // Flags: note that we DO allow for query string data in these regex fragments. foreach ($variations as $_key => $_url) { @@ -252,7 +252,7 @@ public function convertVariationsToHostCachePathRegexFrags(array $variations) if (is_string($_key) && strpos($_key, '::') !== false && strpos($_url, '*') !== false) { list($_feed_type, $_wildcard_regex) = explode('::', $_key, 2); // This regex replaces wildcards. - $_cache_path = $this->plugin->buildCachePath($_url, '', '', $flags | CACHE_PATH_ALLOW_WILDCARDS); + $_cache_path = $this->plugin->buildCachePath($_url, '', '', $flags | $this::CACHE_PATH_ALLOW_WILDCARDS); $_relative_cache_path = preg_replace('/^'.preg_quote($_host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path); $_relative_cache_path_regex = preg_replace('/\\\\\*/', $_wildcard_regex, preg_quote($_relative_cache_path, '/')); } else { diff --git a/src/includes/classes/MenuPage.php b/src/includes/classes/MenuPage.php index 9e2349e..c9a8dda 100644 --- a/src/includes/classes/MenuPage.php +++ b/src/includes/classes/MenuPage.php @@ -1,5 +1,7 @@ GLOBAL_NS, '_wpnonce' => wp_create_nonce())), self_admin_url('/admin.php'))).'">'."\n"; + ' action="'.esc_attr(add_query_arg(urlencode_deep(['page' => GLOBAL_NS, '_wpnonce' => wp_create_nonce()]), self_admin_url('/admin.php'))).'">'."\n"; /* ----------------------------------------------------------------------------------------- */ @@ -28,16 +28,16 @@ public function __construct() if (is_multisite()) { echo ''."\n"; } echo ' '."\n"; echo ' '."\n"; echo '
'."\n"; @@ -47,12 +47,12 @@ public function __construct() echo '
'."\n"; if (IS_PRO && current_user_can($this->plugin->update_cap)) { - echo ' '.__('Pro Updater', 'comet-cache').''."\n"; + echo ' '.__('Pro Updater', 'comet-cache').''."\n"; echo ' '.__('Newsletter', 'comet-cache').''."\n"; echo ' '.__('Beta Testers', 'comet-cache').''."\n"; } if (!IS_PRO) { - echo ' '.__('Preview Pro Features', 'comet-cache').''."\n"; + echo ' '.__('Preview Pro Features', 'comet-cache').''."\n"; echo ' '.__('Pro Upgrade', 'comet-cache').''."\n"; } echo '
'."\n"; @@ -80,7 +80,7 @@ public function __construct() echo ' '.sprintf(__('%1$s™ Pro v%2$s', 'comet-cache'), esc_html(NAME), esc_html(VERSION))."\n"; if ($this->plugin->options['latest_pro_version'] && version_compare(VERSION, $this->plugin->options['latest_pro_version'], '<')) { - echo '('.__('update available', 'comet-cache').')'."\n"; + echo '('.__('update available', 'comet-cache').')'."\n"; } else { echo '('.__('changelog', 'comet-cache').')'."\n"; } @@ -173,7 +173,7 @@ public function __construct() } if (!IS_PRO && $this->plugin->isProPreview()) { echo '
'."\n"; - echo ''.__('close', 'comet-cache').' '."\n"; + echo ''.__('close', 'comet-cache').' '."\n"; echo ' '.sprintf(__('Pro Features (Preview) ~ New option panels below. Please explore before upgrading .
NOTE: the free version of %1$s (this lite version) is more-than-adequate for most sites. Please upgrade only if you desire advanced features or would like to support the developer.', 'comet-cache'), esc_html(NAME))."\n"; echo '
'."\n"; } @@ -251,9 +251,9 @@ public function __construct() /* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ if (IS_PRO || $this->plugin->isProPreview()) { - echo '
'."\n"; + echo '
'."\n"; - echo ' '."\n"; + echo ' '."\n"; echo ' '.__('Manual Cache Clearing', 'comet-cache')."\n"; echo ' '."\n"; @@ -891,7 +891,7 @@ public function __construct() echo '

'.__('You don\'t have to use an .htaccess file to enjoy the performance enhancements provided by this plugin; caching is handled automatically by WordPress/PHP alone. That being said, if you want to take advantage of the additional speed enhancements associated w/ GZIP compression (and we do recommend this), then you WILL need an .htaccess file to accomplish that part.', 'comet-cache').'

'."\n"; echo '

'.sprintf(__('%1$s fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your php.ini file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'.__('If you want to enable GZIP, create an .htaccess file in your WordPress® installation directory, and put the following few lines in it. Alternatively, if you already have an .htaccess file, just add these lines to it, and that is all there is to it. GZIP is now enabled in the recommended way! See also: video about GZIP Compression.', 'comet-cache').'

'."\n"; - echo '
'.esc_html(file_get_contents(dirname(dirname(__FILE__)).'/templates/gzip-htaccess.txt')).'
'."\n"; + echo '
'.esc_html(file_get_contents(dirname(__DIR__).'/templates/gzip-htaccess.txt')).'
'."\n"; echo '
'."\n"; echo '

Or, if your server is missing mod_deflate/mod_filter; open your php.ini file and add this line: zlib.output_compression = on

'."\n"; echo '
'."\n"; @@ -1062,7 +1062,7 @@ public function __construct() echo '
'."\n"; echo '

'.sprintf(__('Export Existing Options from this %1$s Installation?', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo ' '."\n"; echo '

'.sprintf(__('Download your existing options and import them all into another %1$s installation; saves time on future installs.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '
'."\n"; diff --git a/src/includes/classes/Plugin.php b/src/includes/classes/Plugin.php index 264d41a..1a4c131 100644 --- a/src/includes/classes/Plugin.php +++ b/src/includes/classes/Plugin.php @@ -1,5 +1,8 @@ enable_hooks = (boolean) $enable_hooks)) { return; // Stop here; construct without hooks. } /* -------------------------------------------------------------- */ - add_action('after_setup_theme', array($this, 'setup')); - register_activation_hook(PLUGIN_FILE, array($this, 'activate')); - register_deactivation_hook(PLUGIN_FILE, array($this, 'deactivate')); + add_action('plugins_loaded', [$this, 'setup']); + register_activation_hook(PLUGIN_FILE, [$this, 'activate']); + register_deactivation_hook(PLUGIN_FILE, [$this, 'deactivate']); } /** @@ -148,7 +175,7 @@ public function setup() load_plugin_textdomain(SLUG_TD); // Text domain. - $this->pro_only_option_keys = array( + $this->pro_only_option_keys = [ 'cache_max_age_disable_if_load_average_is_gte', 'change_notifications_enable', @@ -216,13 +243,12 @@ public function setup() 'pro_update_username', 'pro_update_password', 'last_pro_stats_log', - ); - $this->default_options = array( + ]; + $this->default_options = [ /* Core/systematic plugin options. */ - 'version' => VERSION, - 'welcomed' => '0', // `0|1` welcomed yet? - 'comet_cache_notice1_enqueued' => '0', // `0|1` announced Comet Cache yet? + 'version' => VERSION, + 'welcomed' => '0', // `0|1` welcomed yet? 'crons_setup' => '0', // A timestamp when last set up. 'crons_setup_on_namespace' => '', // The namespace on which they were set up. @@ -382,7 +408,7 @@ public function setup() /* Related to uninstallation routines. */ 'uninstall_on_deletion' => '0', // `0|1`. - ); + ]; $this->default_options = $this->applyWpFilters(GLOBAL_NS.'_default_options', $this->default_options); $this->options = $this->getOptions(); // Filters, validates, and returns plugin options. @@ -398,13 +424,13 @@ public function setup() } /* -------------------------------------------------------------- */ - add_action('init', array($this, 'checkAdvancedCache')); - add_action('init', array($this, 'checkBlogPaths')); - add_action('init', array($this, 'checkCronSetup'), PHP_INT_MAX); - add_action('wp_loaded', array($this, 'actions')); + add_action('init', [$this, 'checkAdvancedCache']); + add_action('init', [$this, 'checkBlogPaths']); + add_action('init', [$this, 'checkCronSetup'], PHP_INT_MAX); + add_action('wp_loaded', [$this, 'actions']); - add_action('admin_init', array($this, 'checkVersion')); - add_action('admin_init', array($this, 'maybeCheckLatestLiteVersion')); + add_action('admin_init', [$this, 'checkVersion']); + add_action('admin_init', [$this, 'maybeCheckLatestLiteVersion']); @@ -414,56 +440,56 @@ public function setup() - add_action('admin_enqueue_scripts', array($this, 'enqueueAdminStyles')); - add_action('admin_enqueue_scripts', array($this, 'enqueueAdminScripts')); + add_action('admin_enqueue_scripts', [$this, 'enqueueAdminStyles']); + add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']); - add_action('admin_menu', array($this, 'addMenuPages')); - add_action('network_admin_menu', array($this, 'addNetworkMenuPages')); + add_action('admin_menu', [$this, 'addMenuPages']); + add_action('network_admin_menu', [$this, 'addNetworkMenuPages']); - add_action('all_admin_notices', array($this, 'allAdminNotices')); + add_action('all_admin_notices', [$this, 'allAdminNotices']); - add_filter('plugin_action_links_'.plugin_basename(PLUGIN_FILE), array($this, 'addSettingsLink')); + add_filter('plugin_action_links_'.plugin_basename(PLUGIN_FILE), [$this, 'addSettingsLink']); - add_filter('enable_live_network_counts', array($this, 'updateBlogPaths')); + add_filter('enable_live_network_counts', [$this, 'updateBlogPaths']); - add_action('activated_plugin', array($this, 'autoClearOnPluginActivationDeactivation'), 10, 2); - add_action('deactivated_plugin', array($this, 'autoClearOnPluginActivationDeactivation'), 10, 2); - add_action('admin_init', array($this, 'autoClearCacheOnSettingChanges')); - add_action('safecss_save_pre', array($this, 'autoClearCacheOnJetpackCustomCss'), 10, 1); - add_action('upgrader_process_complete', array($this, 'autoClearOnUpgraderProcessComplete'), 10, 2); + add_action('activated_plugin', [$this, 'autoClearOnPluginActivationDeactivation'], 10, 2); + add_action('deactivated_plugin', [$this, 'autoClearOnPluginActivationDeactivation'], 10, 2); + add_action('admin_init', [$this, 'autoClearCacheOnSettingChanges']); + add_action('safecss_save_pre', [$this, 'autoClearCacheOnJetpackCustomCss'], 10, 1); + add_action('upgrader_process_complete', [$this, 'autoClearOnUpgraderProcessComplete'], 10, 2); - add_action('switch_theme', array($this, 'autoClearCache')); - add_action('wp_create_nav_menu', array($this, 'autoClearCache')); - add_action('wp_update_nav_menu', array($this, 'autoClearCache')); - add_action('wp_delete_nav_menu', array($this, 'autoClearCache')); + add_action('switch_theme', [$this, 'autoClearCache']); + add_action('wp_create_nav_menu', [$this, 'autoClearCache']); + add_action('wp_update_nav_menu', [$this, 'autoClearCache']); + add_action('wp_delete_nav_menu', [$this, 'autoClearCache']); - add_action('save_post', array($this, 'autoClearPostCache')); - add_action('delete_post', array($this, 'autoClearPostCache')); - add_action('clean_post_cache', array($this, 'autoClearPostCache')); - add_action('post_updated', array($this, 'autoClearAuthorPageCache'), 10, 3); - add_action('pre_post_update', array($this, 'autoClearPostCacheTransition'), 10, 2); - add_action('woocommerce_product_set_stock', array($this, 'autoClearPostCacheOnWooCommerceSetStock'), 10, 1); + add_action('save_post', [$this, 'autoClearPostCache']); + add_action('delete_post', [$this, 'autoClearPostCache']); + add_action('clean_post_cache', [$this, 'autoClearPostCache']); + add_action('post_updated', [$this, 'autoClearAuthorPageCache'], 10, 3); + add_action('pre_post_update', [$this, 'autoClearPostCacheTransition'], 10, 2); + add_action('woocommerce_product_set_stock', [$this, 'autoClearPostCacheOnWooCommerceSetStock'], 10, 1); - add_action('added_term_relationship', array($this, 'autoClearPostTermsCache'), 10, 1); - add_action('delete_term_relationships', array($this, 'autoClearPostTermsCache'), 10, 1); + add_action('added_term_relationship', [$this, 'autoClearPostTermsCache'], 10, 1); + add_action('delete_term_relationships', [$this, 'autoClearPostTermsCache'], 10, 1); - add_action('trackback_post', array($this, 'autoClearCommentPostCache')); - add_action('pingback_post', array($this, 'autoClearCommentPostCache')); - add_action('comment_post', array($this, 'autoClearCommentPostCache')); - add_action('transition_comment_status', array($this, 'autoClearCommentPostCacheTransition'), 10, 3); + add_action('trackback_post', [$this, 'autoClearCommentPostCache']); + add_action('pingback_post', [$this, 'autoClearCommentPostCache']); + add_action('comment_post', [$this, 'autoClearCommentPostCache']); + add_action('transition_comment_status', [$this, 'autoClearCommentPostCacheTransition'], 10, 3); - add_action('create_term', array($this, 'autoClearCache')); - add_action('edit_terms', array($this, 'autoClearCache')); - add_action('delete_term', array($this, 'autoClearCache')); + add_action('create_term', [$this, 'autoClearCache']); + add_action('edit_terms', [$this, 'autoClearCache']); + add_action('delete_term', [$this, 'autoClearCache']); - add_action('add_link', array($this, 'autoClearCache')); - add_action('edit_link', array($this, 'autoClearCache')); - add_action('delete_link', array($this, 'autoClearCache')); + add_action('add_link', [$this, 'autoClearCache']); + add_action('edit_link', [$this, 'autoClearCache']); + add_action('delete_link', [$this, 'autoClearCache']); if ($this->options['enable'] && $this->applyWpFilters(GLOBAL_NS.'_disable_akismet_comment_nonce', true)) { - add_filter('akismet_comment_nonce', function() { + add_filter('akismet_comment_nonce', function () { return 'disabled-by-'.SLUG_TD; // MUST return a string literal that is not 'true' or '' (an empty string). See }); // See also why the Akismet nonce should be disabled: } @@ -476,8 +502,8 @@ public function setup() /* -------------------------------------------------------------- */ if (!is_multisite() || is_main_site()) { // Main site only. - add_filter('cron_schedules', array($this, 'extendCronSchedules')); - add_action('_cron_'.GLOBAL_NS.'_cleanup', array($this, 'cleanupCache')); + add_filter('cron_schedules', [$this, 'extendCronSchedules']); + add_action('_cron_'.GLOBAL_NS.'_cleanup', [$this, 'cleanupCache']); } diff --git a/src/includes/classes/VsUpgrades.php b/src/includes/classes/VsUpgrades.php index 7586743..cbdb676 100644 --- a/src/includes/classes/VsUpgrades.php +++ b/src/includes/classes/VsUpgrades.php @@ -1,5 +1,5 @@ plugin->readHtaccessFile($htaccess_file)) { - if (is_dir($templates_dir = dirname(dirname(__FILE__)).'/templates/htaccess/back-compat')) { + if (is_dir($templates_dir = dirname(__DIR__).'/templates/htaccess/back-compat')) { $htaccess['file_contents'] = str_replace(file_get_contents($templates_dir.'/v151114.txt'), '', $htaccess['file_contents']); $htaccess['file_contents'] = str_replace(file_get_contents($templates_dir.'/v151114-2.txt'), '', $htaccess['file_contents']); $htaccess['file_contents'] = trim($htaccess['file_contents']); @@ -176,7 +176,7 @@ protected function fromZenCache() wp_clear_scheduled_hook('_cron_zencache_auto_cache'); wp_clear_scheduled_hook('_cron_zencache_cleanup'); } - deactivate_plugins(array('zencache/zencache.php', 'zencache-pro/zencache-pro.php'), true); + deactivate_plugins(['zencache/zencache.php', 'zencache-pro/zencache-pro.php'], true); if (!empty($zencache_options['base_dir'])) { $this->plugin->deleteAllFilesDirsIn(WP_CONTENT_DIR.'/'.trim($zencache_options['base_dir'], '/'), true); @@ -198,11 +198,10 @@ protected function fromZenCache() $this->plugin->activate(); // Reactivate plugin w/ new options. $this->plugin->enqueueMainNotice( - '

'.sprintf(__('Woohoo! %1$s activated. :-)', 'comet-cache'), esc_html(NAME)).'

'. - '

'.sprintf(__('NOTE: Your ZenCache options were preserved by %1$s (for more details, visit the Migration FAQ).'.'', 'comet-cache'), esc_html(NAME), esc_attr(IS_PRO ? 'http://cometcache.com/r/zencache-pro-migration-faq/' : 'https://cometcache.com/r/zencache-migration-faq/')).'

'. - '

'.sprintf(__('To review your configuration, please see: %1$s → Plugin Options.'.'', 'comet-cache'), esc_html(NAME), esc_attr(add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), self_admin_url('/admin.php')))).'

' + '

'.sprintf(__('Woohoo! %1$s activated. :-)', 'comet-cache'), esc_html(NAME)).'

'. + '

'.sprintf(__('NOTE: Your ZenCache options were preserved by %1$s (for more details, visit the Migration FAQ).'.'', 'comet-cache'), esc_html(NAME), esc_attr(IS_PRO ? 'http://cometcache.com/r/zencache-pro-migration-faq/' : 'https://cometcache.com/r/zencache-migration-faq/')).'

'. + '

'.sprintf(__('To review your configuration, please see: %1$s → Plugin Options.'.'', 'comet-cache'), esc_html(NAME), esc_attr(add_query_arg(urlencode_deep(['page' => GLOBAL_NS]), self_admin_url('/admin.php')))).'

' ); } } - } diff --git a/src/includes/closures/Ac/AbortUtils.php b/src/includes/closures/Ac/AbortUtils.php deleted file mode 100644 index f453261..0000000 --- a/src/includes/closures/Ac/AbortUtils.php +++ /dev/null @@ -1,11 +0,0 @@ -maybeIgnoreUserAbort = function () use ($self) { - -}; diff --git a/src/includes/closures/Ac/AcPluginUtils.php b/src/includes/closures/Ac/AcPluginUtils.php deleted file mode 100644 index b5427af..0000000 --- a/src/includes/closures/Ac/AcPluginUtils.php +++ /dev/null @@ -1,25 +0,0 @@ -loadAcPlugins = function () use ($self) { - if (!is_dir(WP_CONTENT_DIR.'/ac-plugins')) { - return; // Nothing to do here. - } - $GLOBALS[GLOBAL_NS.'_advanced_cache'] = $self; // Self reference. - $GLOBALS[GLOBAL_NS.'__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache']; - if (!isset($GLOBALS['zencache__advanced_cache'])) { - $GLOBALS['zencache_advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache']; - $GLOBALS['zencache__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache']; - } - foreach ((array) glob(WP_CONTENT_DIR.'/ac-plugins/*.php') as $_ac_plugin) { - if (is_file($_ac_plugin)) { - include_once $_ac_plugin; - } - } - unset($_ac_plugin); // Houskeeping. -}; diff --git a/src/includes/closures/Ac/BrowserUtils.php b/src/includes/closures/Ac/BrowserUtils.php deleted file mode 100644 index a01229d..0000000 --- a/src/includes/closures/Ac/BrowserUtils.php +++ /dev/null @@ -1,34 +0,0 @@ -maybeStopBrowserCaching = function () use ($self) { - switch ((bool) COMET_CACHE_ALLOW_BROWSER_CACHE) { - - case true: // If global config allows, check exclusions. - - if (isset($_GET[strtolower(SHORT_NAME).'ABC'])) { - if (!filter_var($_GET[strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) { - return $self->sendNoCacheHeaders(); // Disallow. - } // Else, allow client-side caching; because `ABC` is a true-ish value. - // ↑ Note that exclusion patterns are ignored in this case, in favor of `ABC`. - } elseif (COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS && preg_match(COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS, $_SERVER['REQUEST_URI'])) { - return $self->sendNoCacheHeaders(); // Disallow. - } - return; // Allow browser caching; default behavior in this mode. - - case false: // Global config disallows; check inclusions. - - if (isset($_GET[strtolower(SHORT_NAME).'ABC'])) { - if (filter_var($_GET[strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) { - return; // Allow, because `ABC` is a false-ish value. - } // Else, disallow client-side caching; because `ABC` is a true-ish value. - // ↑ Note that inclusion patterns are ignored in this case, in favor of `ABC`. - } - return $self->sendNoCacheHeaders(); // Disallow; default behavior in this mode. - } -}; diff --git a/src/includes/closures/Ac/NcDebugConsts.php b/src/includes/closures/Ac/NcDebugConsts.php deleted file mode 100644 index 5aeb011..0000000 --- a/src/includes/closures/Ac/NcDebugConsts.php +++ /dev/null @@ -1,293 +0,0 @@ -debug_info = array('reason_code' => '', 'reason' => ''); - -/* - * Used to setup debug info (if enabled). - * - * @since 150422 Rewrite. - * - * @param string $reason_code One of the `NC_DEBUG_` constants. - * @param string $reason Optionally override the built-in description with a custom message. - */ -$self->maybeSetDebugInfo = function ($reason_code, $reason = '') use ($self) { - if (!COMET_CACHE_DEBUGGING_ENABLE) { - return; // Nothing to do. - } - $reason = (string) $reason; - if (!($reason_code = (string) $reason_code)) { - return; // Not applicable. - } - $self->debug_info = array('reason_code' => $reason_code, 'reason' => $reason); -}; - -/* - * Echoes `NC_DEBUG_` info in the WordPress `shutdown` phase (if applicable). - * - * @since 150422 Rewrite. - * - * @attaches-to `shutdown` hook in WordPress w/ a late priority. - */ -$self->maybeEchoNcDebugInfo = function () use ($self) { - if (!COMET_CACHE_DEBUGGING_ENABLE) { - return; // Nothing to do. - } - if (is_admin()) { - return; // Not applicable. - } - if (strcasecmp(PHP_SAPI, 'cli') === 0) { - return; // Let's not run the risk here. - } - if ($self->debug_info && $self->hasACacheableContentType() && $self->is_a_wp_content_type) { - echo (string) $self->maybeGetNcDebugInfo($self->debug_info['reason_code'], $self->debug_info['reason']); - } -}; - -/* - * Gets `NC_DEBUG_` info (if applicable). - * - * @since 150422 Rewrite. - * - * @param string $reason_code One of the `NC_DEBUG_` constants. - * @param string $reason Optional; to override the default description with a custom message. - * - * @return string The debug info; i.e. full description (if applicable). - */ -$self->maybeGetNcDebugInfo = function ($reason_code = '', $reason = '') use ($self) { - if (!COMET_CACHE_DEBUGGING_ENABLE) { - return ''; // Not applicable. - } - $reason = (string) $reason; - if (!($reason_code = (string) $reason_code)) { - return ''; // Not applicable. - } - if (!$reason) { - switch ($reason_code) { - case NC_DEBUG_PHP_SAPI_CLI: - $reason = __('because `PHP_SAPI` reports that you are currently running from the command line.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_NO_SERVER_HTTP_HOST: - $reason = __('because `$_SERVER[\'HTTP_HOST\']` is missing from your server configuration.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_NO_SERVER_REQUEST_URI: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` is missing from your server configuration.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT: - if ($self->functionIsPossible('did_action') && did_action('ws_plugin__s2member_during_no_cache_constants')) { - $reason = __('because the s2Member plugin set the PHP constant `COMET_CACHE_ALLOWED` to a boolean-ish `FALSE` value at runtime. The s2Member plugin is serving content that must remain dynamic on this particular page, and therefore this page was intentionally not cached for a very good reason.', 'comet-cache'); - } else { - $reason = __('because the PHP constant `COMET_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); - } - break; // Break switch handler. - - case NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR: - $reason = __('because the environment variable `$_SERVER[\'COMET_CACHE_ALLOWED\']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_DONOTCACHEPAGE_CONSTANT: - $reason = __('because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR: - $reason = __('because the environment variable `$_SERVER[\'DONOTCACHEPAGE\']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_AC_GET_VAR: - $reason = sprintf(__('because `$_GET[\'%1$sAC\']` is set to a boolean-ish FALSE value.', 'comet-cache'), strtolower(SHORT_NAME)); - break; // Break switch handler. - - case NC_DEBUG_UNCACHEABLE_REQUEST: - $reason = __('because `$_SERVER[\'REQUEST_METHOD\']` is `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`. These request methods should never (ever) be cached in any way.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_SELF_SERVE_REQUEST: - $reason = __('because `[current IP address]` === `$_SERVER[\'SERVER_ADDR\']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define(\'LOCALHOST\', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_FEED_REQUEST: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_WP_SYSTEMATICS: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_WP_ADMIN: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` or the `is_admin()` function indicates this is an administrative area of the site.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_MS_FILES: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_IS_LOGGED_IN_USER: - case NC_DEBUG_IS_LIKE_LOGGED_IN_USER: - $reason = __('because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you\'ve left (or replied to) a comment recently. If this message continues, please clear your cookies and try again.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_IS_LOGGED_IN_USER_NONCE: - $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. While your current configuration states that pages SHOULD be cache for logged-in visitors, `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_PAGE_CONTAINS_NONCE: - $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. Note that `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_NO_USER_TOKEN: - $reason = sprintf(__('because the current user appeared to be logged into the site (in one way or another); but %1$s was unable to formulate a User Token for them. Please report this as a possible bug.', 'comet-cache'), NAME); - break; // Break switch handler. - - case NC_DEBUG_GET_REQUEST_QUERIES: - $reason = __('because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_PREVIEW: - $reason = __('because `$_REQUEST` indicates this is simply a preview of something to come.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_EXCLUDED_URIS: - $reason = __('because `$_SERVER[\'REQUEST_URI\']` matches a configured URI Exclusion Pattern on this installation.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_EXCLUDED_AGENTS: - $reason = __('because `$_SERVER[\'HTTP_USER_AGENT\']` matches a configured User-Agent Exclusion Pattern on this installation.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_EXCLUDED_REFS: - $reason = __('because `$_SERVER[\'HTTP_REFERER\']` and/or `$_GET[\'_wp_http_referer\']` matches a configured HTTP Referrer Exclusion Pattern on this installation.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_404_REQUEST: - $reason = __('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_MAINTENANCE_PLUGIN: - $reason = __('because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_OB_ZLIB_CODING_TYPE: - $reason = sprintf(__('because %1$s is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. %1$s is NOT compatible with `ob_gzhandler()` and others like this.', 'comet-cache'), NAME); - break; // Break switch handler. - - case NC_DEBUG_WP_ERROR_PAGE: - $reason = __('because the contents of this document contain ``, which indicates this is an auto-generated WordPress error message.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_UNCACHEABLE_CONTENT_TYPE: - $reason = __('because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_UNCACHEABLE_STATUS: - $reason = __('because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache'); - break; // Break switch handler. - - case NC_DEBUG_1ST_TIME_404_SYMLINK: - $reason = sprintf(__('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it\'s happened on this page. Your current configuration says that 404 errors SHOULD be cached, so %1$s built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don\'t clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request.', 'comet-cache'), NAME); - break; // Break switch handler. - - case NC_DEBUG_EARLY_BUFFER_TERMINATION: - $reason = sprintf(__('because %1$s detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It\'s not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since %1$s is effectively disabled at runtime when this occurs.', 'comet-cache'), NAME); - break; // Break switch handler. - - default: // Default case handler. - $reason = __('due to an unexpected behavior in the application. Please report this as a bug!', 'comet-cache'); - break; // Break switch handler. - } - } - return "\n".''; -}; diff --git a/src/includes/closures/Ac/ObUtils.php b/src/includes/closures/Ac/ObUtils.php deleted file mode 100644 index eec62c0..0000000 --- a/src/includes/closures/Ac/ObUtils.php +++ /dev/null @@ -1,353 +0,0 @@ -protocol = ''; - -/* - * Host token for this request. - * - * @since 150821 Improving multisite compat. - * - * @type string Host token for this request. - */ -$self->host_token = ''; - -/* - * Host base/dir tokens for this request. - * - * @since 150821 Improving multisite compat. - * - * @type string Host base/dir tokens for this request. - */ -$self->host_base_dir_tokens = ''; - -/* - * Calculated version salt; set by site configuration data. - * - * @since 150422 Rewrite. - * - * @type string|mixed Any scalar value does fine. - */ -$self->version_salt = ''; - -/* - * Relative cache path for the current request. - * - * @since 150422 Rewrite. - * - * @type string Cache path for the current request. - */ -$self->cache_path = ''; - -/* - * Absolute cache file path for the current request. - * - * @since 150422 Rewrite. - * - * @type string Absolute cache file path for the current request. - */ -$self->cache_file = ''; - -/* - * Relative 404 cache path for the current request. - * - * @since 150422 Rewrite. - * - * @type string 404 cache path for the current request. - */ -$self->cache_path_404 = ''; - -/* - * Absolute 404 cache file path for the current request. - * - * @since 150422 Rewrite. - * - * @type string Absolute 404 cache file path for the current request. - */ -$self->cache_file_404 = ''; - -/* - * Version salt followed by the current request location. - * - * @since 150422 Rewrite. - * - * @type string Version salt followed by the current request location. - */ -$self->salt_location = ''; - -/* - * Calculated max age; i.e., before expiration. - * - * @since 151002 Load average checks in pro version. - * - * @type integer Calculated max age; i.e., before expiration. - */ -$self->cache_max_age = 0; - -/* - * Start output buffering (if applicable); or serve a cache file (if possible). - * - * @since 150422 Rewrite. - * - * @note This is a vital part of Comet Cache. This method serves existing (fresh) cache files. - * It is also responsible for beginning the process of collecting the output buffer. - */ -$self->maybeStartOutputBuffering = function () use ($self) { - if (strcasecmp(PHP_SAPI, 'cli') === 0) { - return $self->maybeSetDebugInfo(NC_DEBUG_PHP_SAPI_CLI); - } - if (empty($_SERVER['HTTP_HOST']) || !$self->hostToken()) { - return $self->maybeSetDebugInfo(NC_DEBUG_NO_SERVER_HTTP_HOST); - } - if (empty($_SERVER['REQUEST_URI'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_NO_SERVER_REQUEST_URI); - } - if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) { - return $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT); - } - if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) { - return $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR); - } - if (defined('DONOTCACHEPAGE')) { - return $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_CONSTANT); - } - if (isset($_SERVER['DONOTCACHEPAGE'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); - } - if (isset($_GET[strtolower(SHORT_NAME).'AC']) && !filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN)) { - return $self->maybeSetDebugInfo(NC_DEBUG_AC_GET_VAR); - } - if ($self->isUncacheableRequestMethod()) { - return $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_REQUEST); - } - if (isset($_SERVER['SERVER_ADDR']) && $self->currentIp() === $_SERVER['SERVER_ADDR']) { - if ((!IS_PRO || !$self->isAutoCacheEngine()) && !$self->isLocalhost()) { - return $self->maybeSetDebugInfo(NC_DEBUG_SELF_SERVE_REQUEST); - } - } - if (!COMET_CACHE_FEEDS_ENABLE && $self->isFeed()) { - return $self->maybeSetDebugInfo(NC_DEBUG_FEED_REQUEST); - } - if (preg_match('/\/(?:wp\-[^\/]+|xmlrpc)\.php(?:[?]|$)/i', $_SERVER['REQUEST_URI'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_WP_SYSTEMATICS); - } - if (is_admin() || preg_match('/\/wp-admin(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_WP_ADMIN); - } - if (is_multisite() && preg_match('/\/files(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_MS_FILES); - } - if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->isLikeUserLoggedIn()) { - return $self->maybeSetDebugInfo(NC_DEBUG_IS_LIKE_LOGGED_IN_USER); - } - if (!COMET_CACHE_GET_REQUESTS && $self->requestContainsUncacheableQueryVars()) { - return $self->maybeSetDebugInfo(NC_DEBUG_GET_REQUEST_QUERIES); - } - if (!empty($_REQUEST['preview'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_PREVIEW); - } - if (COMET_CACHE_EXCLUDE_URIS && preg_match(COMET_CACHE_EXCLUDE_URIS, $_SERVER['REQUEST_URI'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_URIS); - } - if (COMET_CACHE_EXCLUDE_AGENTS && !empty($_SERVER['HTTP_USER_AGENT']) && (!IS_PRO || !$self->isAutoCacheEngine())) { - if (preg_match(COMET_CACHE_EXCLUDE_AGENTS, $_SERVER['HTTP_USER_AGENT'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_AGENTS); - } - } - if (COMET_CACHE_EXCLUDE_REFS && !empty($_REQUEST['_wp_http_referer'])) { - if (preg_match(COMET_CACHE_EXCLUDE_REFS, stripslashes($_REQUEST['_wp_http_referer']))) { - return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_REFS); - } - } - if (COMET_CACHE_EXCLUDE_REFS && !empty($_SERVER['HTTP_REFERER'])) { - if (preg_match(COMET_CACHE_EXCLUDE_REFS, $_SERVER['HTTP_REFERER'])) { - return $self->maybeSetDebugInfo(NC_DEBUG_EXCLUDED_REFS); - } - } - $self->protocol = $self->isSsl() ? 'https://' : 'http://'; - $self->host_token = $self->hostToken(); - $self->host_base_dir_tokens = $self->hostBaseDirTokens(); - - $self->version_salt = ''; // Initialize the version salt. - - $self->version_salt = $self->applyFilters(get_class($self).'__version_salt', $self->version_salt); - $self->version_salt = $self->applyFilters(GLOBAL_NS.'_version_salt', $self->version_salt); - - $self->cache_path = $self->buildCachePath($self->protocol.$self->host_token.$_SERVER['REQUEST_URI'], '', $self->version_salt); - $self->cache_file = COMET_CACHE_DIR.'/'.$self->cache_path; // Not considering a user cache. That's done in the postload phase. - - $self->cache_path_404 = $self->buildCachePath($self->protocol.$self->host_token.rtrim($self->host_base_dir_tokens, '/').'/'.COMET_CACHE_404_CACHE_FILENAME); - $self->cache_file_404 = COMET_CACHE_DIR.'/'.$self->cache_path_404; // Not considering a user cache at all here--ever. - - $self->salt_location = ltrim($self->version_salt.' '.$self->protocol.$self->host_token.$_SERVER['REQUEST_URI']); - - $self->cache_max_age = strtotime('-'.COMET_CACHE_MAX_AGE); - - if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN === 'postload' && $self->isLikeUserLoggedIn()) { - $self->postload['when_logged_in'] = true; // Enable postload check. - } elseif (is_file($self->cache_file) && (!$self->cache_max_age || filemtime($self->cache_file) >= $self->cache_max_age)) { - list($headers, $cache) = explode('', file_get_contents($self->cache_file), 2); - - $headers_list = $self->headersList(); - foreach (unserialize($headers) as $_header) { - if (!in_array($_header, $headers_list, true) && stripos($_header, 'Last-Modified:') !== 0) { - header($_header); // Only cacheable/safe headers are stored in the cache. - } - } - unset($_header); // Just a little housekeeping. - - if (COMET_CACHE_DEBUGGING_ENABLE && $self->isHtmlXmlDoc($cache)) { - $total_time = number_format(microtime(true) - $self->timer, 5, '.', ''); - $cache .= "\n".''; - // translators: This string is actually NOT translatable because the `__()` function is not available at this point in the processing. - $cache .= "\n".''; - } - exit($cache); // Exit with cache contents. - } else { - ob_start(array($self, 'outputBufferCallbackHandler')); - } - return; // Return value not applicable. -}; - -/* - * Output buffer handler; i.e. the cache file generator. - * - * @note We CANNOT depend on any WP functionality here; it will cause problems. - * Anything we need from WP should be saved in the postload phase as a scalar value. - * - * @since 150422 Rewrite. - * - * @param string $buffer The buffer from {@link \ob_start()}. - * @param int $phase A set of bitmask flags. - * - * @throws \Exception If unable to handle output buffering for any reason. - * - * @return string|bool The output buffer, or `FALSE` to indicate no change. - * - * @attaches-to {@link \ob_start()} - */ -$self->outputBufferCallbackHandler = function ($buffer, $phase) use ($self) { - if (!($phase & PHP_OUTPUT_HANDLER_END)) { - throw new \Exception(sprintf(__('Unexpected OB phase: `%1$s`.', 'comet-cache'), $phase)); - } - AdvCacheBackCompat::zenCacheConstants(); - - $cache = trim((string) $buffer); - - if (!isset($cache[0])) { - return false; // Don't cache an empty buffer. - } - if (!isset($GLOBALS[GLOBAL_NS.'_shutdown_flag'])) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_EARLY_BUFFER_TERMINATION); - } - if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT); - } - if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR); - } - if (defined('DONOTCACHEPAGE')) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_CONSTANT); - } - if (isset($_SERVER['DONOTCACHEPAGE'])) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); - } - if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->is_user_logged_in) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_IS_LOGGED_IN_USER); - } - if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $self->isLikeUserLoggedIn()) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_IS_LIKE_LOGGED_IN_USER); - } - if (!COMET_CACHE_CACHE_NONCE_VALUES && preg_match('/\b(?:_wpnonce|akismet_comment_nonce)\b/', $cache)) { - if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $self->isLikeUserLoggedIn()) { - if (!COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN) { - return (boolean)$self->maybeSetDebugInfo(NC_DEBUG_IS_LOGGED_IN_USER_NONCE); - } - } else { // Use the default debug notice for nonce conflicts. - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_PAGE_CONTAINS_NONCE); - } // An nonce makes the page dynamic; i.e., NOT cache compatible. - } - if ($self->is_404 && !COMET_CACHE_CACHE_404_REQUESTS) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_404_REQUEST); - } - if (stripos($cache, '') !== false) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_WP_ERROR_PAGE); - } - if (!$self->functionIsPossible('http_response_code')) { - if (stripos($cache, 'database error') !== false) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_WP_ERROR_PAGE); - } - } - if (!$self->hasACacheableContentType()) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_CONTENT_TYPE); - } - if (!$self->hasACacheableStatus()) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_UNCACHEABLE_STATUS); - } - if ($self->is_maintenance) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_MAINTENANCE_PLUGIN); - } - if ($self->functionIsPossible('zlib_get_coding_type') && zlib_get_coding_type() - && (!($zlib_oc = ini_get('zlib.output_compression')) || !filter_var($zlib_oc, FILTER_VALIDATE_BOOLEAN))) { - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_OB_ZLIB_CODING_TYPE); - } - # Lock the cache directory while writes take place here. - - $cache_lock = $self->cacheLock(); // Lock cache directory. - - # Construct a temp file for atomic cache writes. - - $cache_file_tmp = $self->addTmpSuffix($self->cache_file); - - # Cache directory checks. The cache file directory is created here if necessary. - - if (!is_dir(COMET_CACHE_DIR) && mkdir(COMET_CACHE_DIR, 0775, true) && !is_file(COMET_CACHE_DIR.'/.htaccess')) { - file_put_contents(COMET_CACHE_DIR.'/.htaccess', $self->htaccess_deny); - } - if (!is_dir($cache_file_dir = dirname($self->cache_file))) { - $cache_file_dir_writable = mkdir($cache_file_dir, 0775, true); - } - if (empty($cache_file_dir_writable) && !is_writable($cache_file_dir)) { - throw new \Exception(sprintf(__('Cache directory not writable. %1$s needs this directory please: `%2$s`. Set permissions to `755` or higher; `777` might be needed in some cases.', 'comet-cache'), NAME, $cache_file_dir)); - } - # This is where a new 404 request might be detected for the first time. - - if ($self->is_404 && is_file($self->cache_file_404)) { - if (!(symlink($self->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $self->cache_file))) { - throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $self->cache_file, $self->cache_file_404, COMET_CACHE_DIR)); - } - $self->cacheUnlock($cache_lock); // Release. - return (boolean) $self->maybeSetDebugInfo(NC_DEBUG_1ST_TIME_404_SYMLINK); - } - /* ------- Otherwise, we need to construct & store a new cache file. ----------------------------------------------- */ - - - - if (COMET_CACHE_DEBUGGING_ENABLE && $self->isHtmlXmlDoc($cache)) { - $total_time = number_format(microtime(true) - $self->timer, 5, '.', ''); // Based on the original timer. - $cache .= "\n".''; - $cache .= "\n".''; - $cache .= "\n".''; - } - if ($self->is_404) { - if (file_put_contents($cache_file_tmp, serialize($self->cacheableHeadersList()).''.$cache) && rename($cache_file_tmp, $self->cache_file_404)) { - if (!(symlink($self->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $self->cache_file))) { - throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $self->cache_file, $self->cache_file_404, COMET_CACHE_DIR)); - } - $self->cacheUnlock($cache_lock); // Release. - return $cache; // Return the newly built cache; with possible debug information also. - } - } elseif (file_put_contents($cache_file_tmp, serialize($self->cacheableHeadersList()).''.$cache) && rename($cache_file_tmp, $self->cache_file)) { - $self->cacheUnlock($cache_lock); // Release. - return $cache; // Return the newly built cache; with possible debug information also. - } - @unlink($cache_file_tmp); // Clean this up (if it exists); and throw an exception with information for the site owner. - throw new \Exception(sprintf(__('%1$s: failed to write cache file for: `%2$s`; possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), NAME, $_SERVER['REQUEST_URI'], COMET_CACHE_DIR)); -}; diff --git a/src/includes/closures/Ac/PostloadUtils.php b/src/includes/closures/Ac/PostloadUtils.php deleted file mode 100644 index 1792f2c..0000000 --- a/src/includes/closures/Ac/PostloadUtils.php +++ /dev/null @@ -1,187 +0,0 @@ -is_wp_loaded_query = false; - -/* - * Is the current request a WordPress 404 error? - * - * @since 150422 Rewrite. - * - * @type bool `TRUE` if is a 404 error; else `FALSE`. - * - * @see wpMainQueryPostload() - */ -$self->is_404 = false; - -/* - * Last HTTP status code passed through {@link \status_header}. - * - * @since 150422 Rewrite. - * - * @type int Last HTTP status code (if applicable). - * - * @see maybeFilterStatusHeaderPostload() - */ -$self->http_status = 0; - -/* - * Is the current request a WordPress content type? - * - * @since 150422 Rewrite. - * - * @type bool `TRUE` if is a WP content type. - * - * @see wpMainQueryPostload() - */ -$self->is_a_wp_content_type = false; - -/* - * Current WordPress {@link \content_url()}. - * - * @since 150422 Rewrite. - * - * @type string Current WordPress {@link \content_url()}. - * - * @see wpMainQueryPostload() - */ -$self->content_url = ''; - -/* - * Flag for {@link \is_user_loged_in()}. - * - * @since 150422 Rewrite. - * - * @type bool `TRUE` if {@link \is_user_loged_in()}; else `FALSE`. - * - * @see wpMainQueryPostload() - */ -$self->is_user_logged_in = false; - -/* - * Flag for {@link \is_maintenance()}. - * - * @since 150422 Rewrite. - * - * @type bool `TRUE` if {@link \is_maintenance()}; else `FALSE`. - * - * @see wpMainQueryPostload() - */ -$self->is_maintenance = false; - -/* - * Array of data targeted at the postload phase. - * - * @since 150422 Rewrite. - * - * @type array Data and/or flags that work with various postload handlers. - */ -$self->postload = array( - - 'filter_status_header' => true, - 'wp_main_query' => true, - 'set_debug_info' => COMET_CACHE_DEBUGGING_ENABLE, -); - - - - - - - - - -/* - * Filters WP {@link \status_header()} (if applicable). - * - * @since 150422 Rewrite. - */ -$self->maybeFilterStatusHeaderPostload = function () use ($self) { - if (empty($self->postload['filter_status_header'])) { - return; // Nothing to do in this case. - } - $_self = $self; // Reference needed below. - - add_filter( - 'status_header', - function ($status_header, $status_code) use ($_self) { - if ($status_code > 0) { - $_self->http_status = (integer) $status_code; - } - return $status_header; - }, - PHP_INT_MAX, - 2 - ); -}; - -/* - * Hooks `NC_DEBUG_` info into the WordPress `shutdown` phase (if applicable). - * - * @since 150422 Rewrite. - */ -$self->maybeSetDebugInfoPostload = function () use ($self) { - if (!COMET_CACHE_DEBUGGING_ENABLE) { - return; // Nothing to do. - } - if (empty($self->postload['set_debug_info'])) { - return; // Nothing to do in this case. - } - if (is_admin()) { - return; // Not applicable. - } - if (strcasecmp(PHP_SAPI, 'cli') === 0) { - return; // Let's not run the risk here. - } - add_action('shutdown', array($self, 'maybeEchoNcDebugInfo'), PHP_INT_MAX - 10); -}; - -/* - * Grab details from WP and the Comet Cache plugin itself, - * after the main query is loaded (if at all possible). - * - * This is where we have a chance to grab any values we need from WordPress; or from the CC plugin. - * It is EXTREMEMLY important that we NOT attempt to grab any object references here. - * Anything acquired in this phase should be stored as a scalar value. - * See {@link outputBufferCallbackHandler()} for further details. - * - * @since 150422 Rewrite. - * - * @attaches-to `wp` hook. - */ -$self->wpMainQueryPostload = function () use ($self) { - if (empty($self->postload['wp_main_query'])) { - return; // Nothing to do in this case. - } - if ($self->is_wp_loaded_query || is_admin()) { - return; // Nothing to do. - } - if (!is_main_query()) { - return; // Not main query. - } - $self->is_wp_loaded_query = true; - $self->is_404 = is_404(); - $self->is_user_logged_in = is_user_logged_in(); - $self->content_url = rtrim(content_url(), '/'); - $self->is_maintenance = $self->functionIsPossible('is_maintenance') && is_maintenance(); - $_self = $self; // Reference for the closure below. - - add_action( - 'template_redirect', - function () use ($_self) { - $_self->is_a_wp_content_type = $_self->is_404 || $_self->is_maintenance - || is_front_page() // See - || is_home() || is_singular() || is_archive() || is_post_type_archive() || is_tax() || is_search() || is_feed(); - }, - 11 - ); -}; diff --git a/src/includes/closures/Ac/ShutdownUtils.php b/src/includes/closures/Ac/ShutdownUtils.php deleted file mode 100644 index 8722571..0000000 --- a/src/includes/closures/Ac/ShutdownUtils.php +++ /dev/null @@ -1,18 +0,0 @@ -registerShutdownFlag = function () use ($self) { - register_shutdown_function(function () { - $GLOBALS[GLOBAL_NS.'_shutdown_flag'] = -1; - }); -}; diff --git a/src/includes/closures/Plugin/ActionUtils.php b/src/includes/closures/Plugin/ActionUtils.php deleted file mode 100644 index a80fcd5..0000000 --- a/src/includes/closures/Plugin/ActionUtils.php +++ /dev/null @@ -1,16 +0,0 @@ -actions = function () use ($self) { - if (!empty($_REQUEST[GLOBAL_NS])) { - new Actions(); - } - -}; diff --git a/src/includes/closures/Plugin/BbPressUtils.php b/src/includes/closures/Plugin/BbPressUtils.php deleted file mode 100644 index b667a00..0000000 --- a/src/includes/closures/Plugin/BbPressUtils.php +++ /dev/null @@ -1,61 +0,0 @@ -isBbPressActive = function () use ($self) { - return class_exists('bbPress'); -}; - -/* - * bbPress post types. - * - * @since 150821 Improving bbPress support. - * - * @return array All bbPress post types. - */ -$self->bbPressPostTypes = function () use ($self) { - if (!$self->isBbPressActive()) { - return array(); - } - if (!is_null($types = &$self->cacheKey('bbPressPostTypes'))) { - return $types; // Already did this. - } - $types = array(); // Initialize. - $types[] = bbp_get_forum_post_type(); - $types[] = bbp_get_topic_post_type(); - $types[] = bbp_get_reply_post_type(); - - return $types; -}; - -/* - * bbPress post statuses. - * - * @since 150821 Improving bbPress support. - * - * @return array All bbPress post statuses. - */ -$self->bbPressStatuses = function () use ($self) { - if (!$self->isBbPressActive()) { - return array(); - } - if (!is_null($statuses = &$self->cacheKey('bbPressStatuses'))) { - return $statuses; // Already did this. - } - $statuses = array(); // Initialize. - - foreach (get_post_stati(array(), 'objects') as $_key => $_status) { - if (isset($_status->label_count['domain']) && $_status->label_count['domain'] === 'bbpress') { - $statuses[] = $_status->name; - } - } - unset($_key, $_status); // Housekeeping. - - return $statuses; -}; diff --git a/src/includes/closures/Plugin/CleanupUtils.php b/src/includes/closures/Plugin/CleanupUtils.php deleted file mode 100644 index 5c67a9b..0000000 --- a/src/includes/closures/Plugin/CleanupUtils.php +++ /dev/null @@ -1,19 +0,0 @@ -cleanupCache = function () use ($self) { - if (!$self->options['enable']) { - return; // Nothing to do. - } - - - - $self->wurgeCache(); // Purge now. -}; diff --git a/src/includes/closures/Plugin/CondUtils.php b/src/includes/closures/Plugin/CondUtils.php deleted file mode 100644 index 8069efe..0000000 --- a/src/includes/closures/Plugin/CondUtils.php +++ /dev/null @@ -1,13 +0,0 @@ -isProPreview = function () use ($self) { - return !empty($_REQUEST[GLOBAL_NS.'_pro_preview']); -}; diff --git a/src/includes/closures/Plugin/CronUtils.php b/src/includes/closures/Plugin/CronUtils.php deleted file mode 100644 index 4a68b80..0000000 --- a/src/includes/closures/Plugin/CronUtils.php +++ /dev/null @@ -1,80 +0,0 @@ -extendCronSchedules = function ($schedules) use ($self) { - $schedules['every15m'] = array( - 'interval' => 900, - 'display' => __('Every 15 Minutes', 'comet-cache'), - ); - return $schedules; -}; - -/* - * Checks Cron setup, validates schedules, and reschedules events if necessary. - * - * @attaches-to `init` hook. - * - * @since 151220 Improving WP Cron setup and validation of schedules - */ -$self->checkCronSetup = function () use ($self) { - if ($self->options['crons_setup'] < 1439005906 - || $self->options['crons_setup_on_namespace'] !== __NAMESPACE__ - || $self->options['crons_setup_with_cache_cleanup_schedule'] !== $self->options['cache_cleanup_schedule'] - || $self->options['crons_setup_on_wp_with_schedules'] !== sha1(serialize(wp_get_schedules())) - || !wp_next_scheduled('_cron_'.GLOBAL_NS.'_cleanup') - - ) { - - wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); - wp_schedule_event(time() + 60, $self->options['cache_cleanup_schedule'], '_cron_'.GLOBAL_NS.'_cleanup'); - - - - $self->updateOptions( - array( - 'crons_setup' => time(), - 'crons_setup_on_namespace' => __NAMESPACE__, - 'crons_setup_with_cache_cleanup_schedule' => $self->options['cache_cleanup_schedule'], - 'crons_setup_on_wp_with_schedules' => sha1(serialize(wp_get_schedules())) - ) - ); - } -}; - -/* - * Resets `crons_setup` and clears WP-Cron schedules. - * - * @since 151220 Fixing bug with Auto-Cache Engine cron disappearing in some scenarios - * - * @note This MUST happen upon uninstall and deactivation due to buggy WP_Cron behavior. Events with a custom schedule will disappear when plugin is not active (see http://bit.ly/1lGdr78). - */ -$self->resetCronSetup = function ( ) use ($self) { - if (is_multisite()) { // Main site CRON jobs. - switch_to_blog(get_current_site()->blog_id); - - wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); - restore_current_blog(); // Restore current blog. - } else { // Standard WP installation. - - wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); - } - $self->updateOptions( - array( // Reset so that crons are rescheduled upon next activation - 'crons_setup' => $self->default_options['crons_setup'], - 'crons_setup_on_namespace' => $self->default_options['crons_setup_on_namespace'], - 'crons_setup_with_cache_cleanup_schedule' => $self->default_options['crons_setup_with_cache_cleanup_schedule'], - 'crons_setup_on_wp_with_schedules' => $self->default_options['crons_setup_on_wp_with_schedules'] - ) - ); -}; diff --git a/src/includes/closures/Plugin/DbUtils.php b/src/includes/closures/Plugin/DbUtils.php deleted file mode 100644 index bc32e48..0000000 --- a/src/includes/closures/Plugin/DbUtils.php +++ /dev/null @@ -1,13 +0,0 @@ -wpdb = function () use ($self) { - return $GLOBALS['wpdb']; -}; diff --git a/src/includes/closures/Plugin/DirUtils.php b/src/includes/closures/Plugin/DirUtils.php deleted file mode 100644 index c341e3d..0000000 --- a/src/includes/closures/Plugin/DirUtils.php +++ /dev/null @@ -1,79 +0,0 @@ -wpContentBaseDirTo = function ($rel_dir_file) use ($self) { - $rel_dir_file = trim((string) $rel_dir_file, '\\/'." \t\n\r\0\x0B"); - - if (empty($self->options['base_dir'])) { - throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache')); - } - $wp_content_base_dir_to = WP_CONTENT_DIR.'/'.$self->options['base_dir']; - - if (isset($rel_dir_file[0])) { - $wp_content_base_dir_to .= '/'.$rel_dir_file; - } - return $wp_content_base_dir_to; -}; - -/* - * This constructs a relative/base directory path (no leading/trailing slashes). - * Always relative to {@link \WP_CONTENT_DIR}. Depends on the configured `base_dir` option value. - * - * @since 150422 Rewrite. - * - * @param string $rel_dir_file A sub-directory or file; relative location please. - * - * @throws \Exception If `base_dir` is empty when this method is called upon; - * i.e. if you attempt to call upon this method before {@link setup()} runs. - * - * @return string The relative/base directory path to `$rel_dir_file`. - */ -$self->basePathTo = function ($rel_dir_file) use ($self) { - $rel_dir_file = trim((string) $rel_dir_file, '\\/'." \t\n\r\0\x0B"); - - if (empty($self->options['base_dir'])) { - throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache')); - } - $base_path_to = $self->options['base_dir']; - - if (isset($rel_dir_file[0])) { - $base_path_to .= '/'.$rel_dir_file; - } - return $base_path_to; -}; - -/** - * Get the absolute filesystem path to the root of the WordPress installation - * - * Copied verbatim from get_home_path() in wp-admin/includes/file.php - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @return string Full filesystem path to the root of the WordPress installation - */ -$self->wpHomePath = function () use ($self) { - $home = set_url_scheme( get_option( 'home' ), 'http' ); - $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); - if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { - $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ - $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); - $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); - $home_path = trailingslashit( $home_path ); - } else { - $home_path = ABSPATH; - } - return str_replace( '\\', '/', $home_path ); -}; diff --git a/src/includes/closures/Plugin/HtaccessUtils.php b/src/includes/closures/Plugin/HtaccessUtils.php deleted file mode 100644 index 88ed78b..0000000 --- a/src/includes/closures/Plugin/HtaccessUtils.php +++ /dev/null @@ -1,273 +0,0 @@ -htaccess_marker = 'WmVuQ2FjaGU'; - -/* -* Plugin options that have associated htaccess rules. -* -* @since 160103 Improving `.htaccess` tweaks. -* -* @return array Plugin options that have associated htaccess rules -* -* @note We keep track of this to avoid the issue described here: http://git.io/vEFIH -*/ -$self->options_with_htaccess_rules = array('cdn_enable'); - -/* - * Add template blocks to `/.htaccess` file. - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @return boolean True if added successfully. - * - * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT - */ -$self->addWpHtaccess = function () use ($self) { - global $is_apache; - - if (!$is_apache) { - return false; // Not running the Apache web server. - } - if (!$self->options['enable']) { - return true; // Nothing to do. - } - if (!$self->needHtaccessRules()) { - if($self->findHtaccessMarker()) { // Do we need to clean up previously added rules? - $self->removeWpHtaccess(); // Fail silently since we don't need rules in place. - } - return true; // Nothing to do; no options enabled that require htaccess rules. - } - if (!$self->removeWpHtaccess()) { - return false; // Unable to remove. - } - if (!($htaccess = $self->readHtaccessFile())) { - return false; // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. - } - - $template_blocks = ''; // Initialize. - if (is_dir($templates_dir = dirname(dirname(dirname(__FILE__))).'/templates/htaccess')) { - foreach (scandir($templates_dir) as $_template_file) { - switch ($_template_file) { - - } - } - unset($_template_file); // Housekeeping. - } - - if(empty($template_blocks)) { // Do we need to add anything to htaccess? - $self->closeHtaccessFile($htaccess); // No need to write to htaccess file in this case. - return true; // Nothing to do, but no failures either. - } - - $template_header = '# BEGIN '.NAME.' '.$self->htaccess_marker.' (the '.$self->htaccess_marker.' marker is required for '.NAME.'; do not remove)'."\n"; - $template_footer = '# END '.NAME.' '.$self->htaccess_marker; - $htaccess['file_contents'] = $template_header.trim($template_blocks)."\n".$template_footer."\n\n".$htaccess['file_contents']; - - if (!$self->writeHtaccessFile($htaccess, true)) { - return false; // Failure; could not write changes. - } - - return true; // Added successfully. -}; - -/* - * Remove template blocks from `/.htaccess` file. - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @return boolean True if removed successfully. - * - * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT - */ -$self->removeWpHtaccess = function () use ($self) { - global $is_apache; - - if (!$is_apache) { - return false; // Not running the Apache web server. - } - if (!($htaccess_file = $self->findHtaccessFile())) { - return true; // File does not exist. - } - if (!$self->findHtaccessMarker()) { - return true; // Template blocks are already gone. - } - if (!($htaccess = $self->readHtaccessFile())) { - return false; // Failure; could not read file, create file, or invalid UTF8 encountered, file may be corrupt. - } - - $regex = '/#\s*BEGIN\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'.*?#\s*END\s+'.preg_quote(NAME, '/').'\s+'.$self->htaccess_marker.'\s*/is'; - $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']); - - if (!$self->writeHtaccessFile($htaccess, false)) { - return false; // Failure; could not write changes. - } - - return true; // Removed successfully. -}; - -/* - * Finds absolute server path to `/.htaccess` file. - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @return string Absolute server path to `/.htaccess` file; - * else an empty string if unable to locate the file. - */ -$self->findHtaccessFile = function () use ($self) { - $file = ''; // Initialize. - $home_path = $self->wpHomePath(); - - if (is_file($htaccess_file = $home_path.'.htaccess')) { - $file = $htaccess_file; - } - return $file; -}; - -/* - * Determines if there are any plugin options enabled that require htaccess rules to be added. - * - * @since 160103 Improving `.htaccess` tweaks. - * - * @return bool True when an option is enabled that requires htaccess rules, false otherwise. - */ -$self->needHtaccessRules = function () use ($self) { - if(!is_array($self->options_with_htaccess_rules)) { - return false; // Nothing to do. - } - foreach ($self->options_with_htaccess_rules as $option) { - if ($self->options[$option]) { - return true; // Yes, there are options enabled that require htaccess rules. - } - } - return false; // No, there are no options enabled that require htaccess rules. -}; - -/* - * Utility method used to check if htaccess file contains $htaccess_marker - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. - * - * @return bool False on failure or when marker does not exist in htaccess, true otherwise. - */ -$self->findHtaccessMarker = function ($htaccess_marker = '') use ($self) { - if (!($htaccess_file = $self->findHtaccessFile())) { - return false; // File does not exist. - } - if (!is_readable($htaccess_file)) { - return false; // Not possible. - } - if (($htaccess_file_contents = file_get_contents($htaccess_file)) === false) { - return false; // Failure; could not read file. - } - if (empty($htaccess_marker)) { - $htaccess_marker = $self->htaccess_marker; - } - if (stripos($htaccess_file_contents, $htaccess_marker) === false) { - return false; // Htaccess marker is missing - } - - return true; // Htaccess has the marker -}; - -/* - * Gets contents of `/.htaccess` file with exclusive lock to read+write. If file doesn't exist, we attempt to create it. - * - * @since 151220 Improving `.htaccess` utils. - * - * @param string $htaccess_file Absolute path to the htaccess file. Optional. - * If not provided, we attempt to find it or create it if it doesn't exist. - * - * @return array|bool Returns an array with data necessary to call $self->writeHtaccessFile(): - * `fp` a file pointer resource, `file_contents` a string. Returns `false` on failure. - * - * @note If a call to this method is not followed by a call to $self->writeHtaccessFile(), - * you must make sure that you unlock and close the `fp` resource yourself. - */ -$self->readHtaccessFile = function ($htaccess_file = '') use ($self) { - - if (empty($htaccess_file) && !($htaccess_file = $self->findHtaccessFile())) { - if (!is_writable($self->wpHomePath()) || file_put_contents($htaccess_file = $self->wpHomePath().'.htaccess', '') === false) { - return false; // Unable to find and/or create `.htaccess`. - } // If it doesn't exist, we create the `.htaccess` file here. - } - if (!is_readable($htaccess_file) || !is_writable($htaccess_file) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) { - return false; // Not possible. - } - if (!($fp = fopen($htaccess_file, 'rb+')) || !flock($fp, LOCK_EX)) { - fclose($fp); // Just in case we opened it before failing to obtain a lock. - return false; // Failure; could not open file and obtain an exclusive lock. - } - if (($file_contents = fread($fp, filesize($htaccess_file))) && ($file_contents === wp_check_invalid_utf8($file_contents))) { - rewind($fp); // Rewind pointer to beginning of file. - return compact('fp', 'file_contents'); - } else { // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. - flock($fp, LOCK_UN); - fclose($fp); - return false; - } -}; - -/* - * Writes to `/.htaccess` file using provided file pointer. - * - * @since 151220 Improving `.htaccess` utils. - * - * @param array $htaccess Array containing `fp` file resource pointing to htaccess file and `file_contents` to write to file. - * @param bool $require_marker Whether or not to require the marker be present in contents before writing. - * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. - * - * @return bool True on success, false on failure. - */ -$self->writeHtaccessFile = function (array $htaccess, $require_marker = true, $htaccess_marker = '') use ($self) { - - if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { - return false; // Not possible. - } - if (!is_resource($htaccess['fp'])) { - return false; - } - $htaccess_marker = $htaccess_marker ?: $self->htaccess_marker; - - $_have_marker = stripos($htaccess['file_contents'], $htaccess_marker); - - // Note: rewind() necessary here because we fread() above. - if (($require_marker && $_have_marker === false) || !rewind($htaccess['fp']) || !ftruncate($htaccess['fp'], 0) || !fwrite($htaccess['fp'], $htaccess['file_contents'])) { - flock($htaccess['fp'], LOCK_UN); - fclose($htaccess['fp']); - return false; // Failure; could not write changes. - } - fflush($htaccess['fp']); - flock($htaccess['fp'], LOCK_UN); - fclose($htaccess['fp']); - - return true; -}; - -/* - * Utility method used to unlock and close htaccess file resource. - * - * @since 151114 Adding `.htaccess` tweaks. - * - * @param array $htaccess Array containing at least an `fp` file resource pointing to htaccess file. - * - * @return bool False on failure, true otherwise. - */ -$self->closeHtaccessFile = function (array $htaccess) use ($self) { - if (!is_resource($htaccess['fp'])) { - return false; // Failure; requires a valid file resource. - } - flock($htaccess['fp'], LOCK_UN); - fclose($htaccess['fp']); - - return true; -}; diff --git a/src/includes/closures/Plugin/InstallUtils.php b/src/includes/closures/Plugin/InstallUtils.php deleted file mode 100644 index 8054586..0000000 --- a/src/includes/closures/Plugin/InstallUtils.php +++ /dev/null @@ -1,535 +0,0 @@ -activate = function () use ($self) { - $self->setup(); // Ensure setup is complete. - - if (!$self->options['welcomed'] && !$self->options['enable']) { - $settings_url = add_query_arg(urlencode_deep(array('page' => GLOBAL_NS)), network_admin_url('/admin.php')); - $self->enqueueMainNotice(sprintf(__('%1$s successfully installed! :-) Please enable caching and review options.', 'comet-cache'), esc_html(NAME), esc_attr($settings_url)), array('push_to_top' => true)); - $self->updateOptions(array('welcomed' => '1')); - } - - if (!$self->options['enable']) { - return; // Nothing to do. - } - - $self->addWpCacheToWpConfig(); - $self->addWpHtaccess(); - $self->addAdvancedCache(); - $self->updateBlogPaths(); - $self->autoClearCache(); -}; - -/* - * Check current plugin version that is installed in WP. - * - * @since 150422 Rewrite. - * - * @attaches-to `admin_init` hook. - */ -$self->checkVersion = function () use ($self) { - $prev_version = $self->options['version']; - if (version_compare($prev_version, VERSION, '>=')) { - return; // Nothing to do; up-to-date. - } - $self->updateOptions(array('version' => VERSION)); - - new VsUpgrades($prev_version); - - if ($self->options['enable']) { - $self->addWpCacheToWpConfig(); - $self->addWpHtaccess(); - $self->addAdvancedCache(); - $self->updateBlogPaths(); - } - $self->wipeCache(); // Fresh start now. - - $self->enqueueMainNotice(sprintf(__('%1$s: detected a new version of itself. Recompiling w/ latest version... wiping the cache... all done :-)', 'comet-cache'), esc_html(NAME)), array('push_to_top' => true)); -}; - -/* - * Plugin deactivation hook. - * - * @since 150422 Rewrite. - * - * @attaches-to {@link \register_deactivation_hook()} - */ -$self->deactivate = function () use ($self) { - $self->setup(); // Ensure setup is complete. - - $self->removeWpCacheFromWpConfig(); - $self->removeWpHtaccess(); - $self->removeAdvancedCache(); - $self->clearCache(); - $self->resetCronSetup(); -}; - -/* - * Plugin uninstall hook. - * - * @since 150422 Rewrite. - */ -$self->uninstall = function () use ($self) { - $self->setup(); // Ensure setup is complete. - - if (!defined('WP_UNINSTALL_PLUGIN')) { - return; // Disallow. - } - if (empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) { - return; // Not uninstalling. - } - if (!current_user_can($self->uninstall_cap)) { - return; // Extra layer of security. - } - $self->removeWpCacheFromWpConfig(); - $self->removeWpHtaccess(); - $self->removeAdvancedCache(); - $self->wipeCache(); - $self->resetCronSetup(); - - if (!$self->options['uninstall_on_deletion']) { - return; // Nothing to do here. - } - $self->deleteAdvancedCache(); - $self->deleteBaseDir(); - - $wpdb = $self->wpdb(); // WordPress DB. - $like = '%'.$wpdb->esc_like(GLOBAL_NS).'%'; - - if (is_multisite()) { // Site options for a network installation. - $wpdb->query('DELETE FROM `'.esc_sql($wpdb->sitemeta).'` WHERE `meta_key` LIKE \''.esc_sql($like).'\''); - - switch_to_blog(get_current_site()->blog_id); // In case it started as a standard WP installation. - $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\''); - restore_current_blog(); // Restore current blog. - // - } else { // Standard WP installation. - $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\''); - } -}; - -/* - * Adds `define('WP_CACHE', TRUE);` to the `/wp-config.php` file. - * - * @since 150422 Rewrite. - * - * @return string The new contents of the updated `/wp-config.php` file; - * else an empty string if unable to add the `WP_CACHE` constant. - */ -$self->addWpCacheToWpConfig = function () use ($self) { - if (!$self->options['enable']) { - return ''; // Nothing to do. - } - if (!($wp_config_file = $self->findWpConfigFile())) { - return ''; // Unable to find `/wp-config.php`. - } - if (!is_readable($wp_config_file)) { - return ''; // Not possible. - } - if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { - return ''; // Failure; could not read file. - } - if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) { - return ''; // Failure; file empty - } - if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[1-9][0-9\.]*|TRUE|([\'"])(?:[^0\'"]|[^\'"]{2,})\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace)) { - return $wp_config_file_contents; // It's already in there; no need to modify this file. - } - if (!($wp_config_file_contents = $self->removeWpCacheFromWpConfig())) { - return ''; // Unable to remove previous value. - } - if (!($wp_config_file_contents = preg_replace('/^\s*(\<\?php|\<\?)\s+/i', '${1}'."\n"."define('WP_CACHE', TRUE);"."\n", $wp_config_file_contents, 1))) { - return ''; // Failure; something went terribly wrong here. - } - if (strpos($wp_config_file_contents, "define('WP_CACHE', TRUE);") === false) { - return ''; // Failure; unable to add; unexpected PHP code. - } - if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { - return ''; // We may NOT edit any files. - } - if (!is_writable($wp_config_file)) { - return ''; // Not possible. - } - if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { - return ''; // Failure; could not write changes. - } - return $wp_config_file_contents; -}; - -/* - * Removes `define('WP_CACHE', TRUE);` from the `/wp-config.php` file. - * - * @since 150422 Rewrite. - * - * @return string The new contents of the updated `/wp-config.php` file; - * else an empty string if unable to remove the `WP_CACHE` constant. - */ -$self->removeWpCacheFromWpConfig = function () use ($self) { - if (!($wp_config_file = $self->findWpConfigFile())) { - return ''; // Unable to find `/wp-config.php`. - } - if (!is_readable($wp_config_file)) { - return ''; // Not possible. - } - if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { - return ''; // Failure; could not read file. - } - if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) { - return ''; // Failure; file empty - } - if (!preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents_no_whitespace)) { - return $wp_config_file_contents; // Already gone. - } - if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:0|FALSE|NULL|([\'"])0?\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace) && !is_writable($wp_config_file)) { - return $wp_config_file_contents; // It's already disabled, and since we can't write to this file let's let this slide. - } - if (!($wp_config_file_contents = preg_replace('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[0-9\.]+|TRUE|FALSE|NULL|([\'"])[^\'"]*\\2)\s*\)\s*;/i', '', $wp_config_file_contents))) { - return ''; // Failure; something went terribly wrong here. - } - if (preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents)) { - return ''; // Failure; perhaps the `/wp-config.php` file contains syntax we cannot remove safely. - } - if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { - return ''; // We may NOT edit any files. - } - if (!is_writable($wp_config_file)) { - return ''; // Not possible. - } - if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { - return ''; // Failure; could not write changes. - } - return $wp_config_file_contents; -}; - -/* - * Checks to make sure the `advanced-cache.php` file still exists; - * and if it doesn't, the `advanced-cache.php` is regenerated automatically. - * - * @since 150422 Rewrite. - * - * @attaches-to `init` hook. - * - * @note This runs so that remote deployments which completely wipe out an - * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache - * to stop functioning due to the lack of an `advanced-cache.php` file, which is generated by Comet Cache. - * - * For instance, if you have a Git repo with all of your site files; when you push those files - * to your website to deploy them, you most likely do NOT have the `advanced-cache.php` file. - * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active) - * we simply regenerate the file automatically to keep Comet Cache running. - */ -$self->checkAdvancedCache = function () use ($self) { - if (!$self->options['enable']) { - return; // Nothing to do. - } - if (!empty($_REQUEST[GLOBAL_NS])) { - return; // Skip on plugin actions. - } - $cache_dir = $self->cacheDir(); - $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; - $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; - - // Fixes zero-byte advanced-cache.php bug related to migrating from ZenCache - // See: - - // Also fixes a missing `define('WP_CACHE', TRUE)` bug related to migrating from ZenCache - // See - - if (!is_file($advanced_cache_check_file) || !is_file($advanced_cache_file) || filesize($advanced_cache_file) === 0) { - $self->addAdvancedCache(); - $self->addWpCacheToWpConfig(); - } -}; - -/* - * Creates and adds the `advanced-cache.php` file. - * - * @since 150422 Rewrite. - * - * @return bool|null `TRUE` on success. `FALSE` or `NULL` on failure. - * A special `NULL` return value indicates success with a single failure - * that is specifically related to the `[SHORT_NAME]-advanced-cache` file. - */ -$self->addAdvancedCache = function () use ($self) { - if (!$self->removeAdvancedCache()) { - return false; // Still exists. - } - $cache_dir = $self->cacheDir(); - $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; - $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; - $advanced_cache_template = dirname(dirname(dirname(__FILE__))).'/templates/advanced-cache.txt'; - - if (is_file($advanced_cache_file) && !is_writable($advanced_cache_file)) { - return false; // Not possible to create. - } - if (!is_file($advanced_cache_file) && !is_writable(dirname($advanced_cache_file))) { - return false; // Not possible to create. - } - if (!is_file($advanced_cache_template) || !is_readable($advanced_cache_template)) { - return false; // Template file is missing; or not readable. - } - if (!($advanced_cache_contents = file_get_contents($advanced_cache_template))) { - return false; // Template file is missing; or is not readable. - } - $possible_advanced_cache_constant_key_values = array_merge( - $self->options, // The following additional keys are dynamic. - array('cache_dir' => $self->basePathTo($self->cache_sub_dir), - - ) - ); - if ($self->applyWpFilters(GLOBAL_NS.'_exclude_uris_client_side_too', true)) { - $possible_advanced_cache_constant_key_values['exclude_client_side_uris'] .= "\n".$self->options['exclude_uris']; - } - foreach ($possible_advanced_cache_constant_key_values as $_option => $_value) { - $_value = (string) $_value; // Force string. - - switch ($_option) { - case 'exclude_uris': // Converts to regex (caSe insensitive). - case 'exclude_client_side_uris': // Converts to regex (caSe insensitive). - case 'exclude_refs': // Converts to regex (caSe insensitive). - case 'exclude_agents': // Converts to regex (caSe insensitive). - - - - $_value = "'".$self->escSq($self->lineDelimitedPatternsToRegex($_value))."'"; - - break; // Break switch handler. - - - - default: // Default case handler. - - $_value = "'".$self->escSq($_value)."'"; - - break; // Break switch handler. - } - $advanced_cache_contents = // Fill replacement codes. - str_ireplace( - array( - "'%%".GLOBAL_NS.'_'.$_option."%%'", - "'%%".GLOBAL_NS.'_'.preg_replace('/^cache_/i', '', $_option)."%%'", - ), - $_value, - $advanced_cache_contents - ); - } - unset($_option, $_value, $_values, $_response); // Housekeeping. - - if (strpos(PLUGIN_FILE, WP_CONTENT_DIR) === 0) { - $plugin_file = "WP_CONTENT_DIR.'".$self->escSq(str_replace(WP_CONTENT_DIR, '', PLUGIN_FILE))."'"; - } else { - $plugin_file = "'".$self->escSq(PLUGIN_FILE)."'"; // Full absolute path. - } - // Make it possible for the `advanced-cache.php` handler to find the plugin directory reliably. - $advanced_cache_contents = str_ireplace("'%%".GLOBAL_NS."_PLUGIN_FILE%%'", $plugin_file, $advanced_cache_contents); - - // Ignore; this is created by Comet Cache; and we don't need to obey in this case. - #if(defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) - # return FALSE; // We may NOT edit any files. - - if (!file_put_contents($advanced_cache_file, $advanced_cache_contents)) { - return false; // Failure; could not write file. - } - $cache_lock = $self->cacheLock(); // Lock cache. - - if (!is_dir($cache_dir)) { - mkdir($cache_dir, 0775, true); - } - if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) { - file_put_contents($cache_dir.'/.htaccess', $self->htaccess_deny); - } - if (!is_dir($cache_dir) || !is_writable($cache_dir) || !is_file($cache_dir.'/.htaccess') || !file_put_contents($advanced_cache_check_file, time())) { - $self->cacheUnlock($cache_lock); // Release. - return; // Special return value (NULL). - } - $self->cacheUnlock($cache_lock); // Release. - - $self->clearAcDropinFromOpcacheByForce(); - - return true; -}; - -/* - * Removes the `advanced-cache.php` file. - * - * @since 150422 Rewrite. - * - * @return bool `TRUE` on success. `FALSE` on failure. - * - * @note The `advanced-cache.php` file is NOT actually deleted by this routine. - * Instead of deleting the file, we simply empty it out so that it's `0` bytes in size. - * - * The reason for this is to preserve any file permissions set by the site owner. - * If the site owner previously allowed this specific file to become writable, we don't want to - * lose that permission by deleting the file; forcing the site owner to do it all over again later. - * - * An example of where this is useful is when a site owner deactivates the CC plugin, - * but later they decide that CC really is the most awesome plugin in the world and they turn it back on. - */ -$self->removeAdvancedCache = function () use ($self) { - $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; - - if (!is_file($advanced_cache_file)) { - return true; // Already gone. - } - if (is_readable($advanced_cache_file) && filesize($advanced_cache_file) === 0) { - return true; // Already gone; i.e. it's empty already. - } - if (!is_writable($advanced_cache_file)) { - return false; // Not possible. - } - /* Empty the file only. This way permissions are NOT lost in cases where - a site owner makes this specific file writable for Comet Cache. */ - if (file_put_contents($advanced_cache_file, '') !== 0) { - return false; // Failure. - } - $self->clearAcDropinFromOpcacheByForce(); - - return true; -}; - -/* - * Deletes the `advanced-cache.php` file. - * - * @since 150422 Rewrite. - * - * @return bool `TRUE` on success. `FALSE` on failure. - * - * @note The `advanced-cache.php` file is deleted by this routine. - */ -$self->deleteAdvancedCache = function () use ($self) { - $cache_dir = $self->cacheDir(); - $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; - $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; - - if (is_file($advanced_cache_file)) { - if (!is_writable($advanced_cache_file) || !unlink($advanced_cache_file)) { - return false; // Not possible; or outright failure. - } - } - if (is_file($advanced_cache_check_file)) { - if (!is_writable($advanced_cache_check_file) || !unlink($advanced_cache_check_file)) { - return false; // Not possible; or outright failure. - } - } - $self->clearAcDropinFromOpcacheByForce(); - - return true; // Deletion success. -}; - -/* - * Checks to make sure the `[SHORT_NAME]-blog-paths` file still exists; - * and if it doesn't, the `[SHORT_NAME]-blog-paths` file is regenerated automatically. - * - * @since 150422 Rewrite. - * - * @attaches-to `init` hook. - * - * @note This runs so that remote deployments which completely wipe out an - * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache - * to stop functioning due to the lack of a `[SHORT_NAME]-blog-paths` file, which is generated by Comet Cache. - * - * For instance, if you have a Git repo with all of your site files; when you push those files - * to your website to deploy them, you most likely do NOT have the `[SHORT_NAME]-blog-paths` file. - * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active) - * we simply regenerate the file automatically to keep Comet Cache running. - */ -$self->checkBlogPaths = function () use ($self) { - if (!$self->options['enable']) { - return; // Nothing to do. - } - if (!is_multisite()) { - return; // N/A. - } - if (!empty($_REQUEST[GLOBAL_NS])) { - return; // Skip on plugin actions. - } - $cache_dir = $self->cacheDir(); - $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths'; - - if (!is_file($blog_paths_file)) { - $self->updateBlogPaths(); - } -}; - -/* - * Creates and/or updates the `[SHORT_NAME]-blog-paths` file. - * - * @since 150422 Rewrite. - * - * @attaches-to `enable_live_network_counts` filter. - * - * @param mixed $enable_live_network_counts Optional, defaults to a `NULL` value. - * - * @return mixed The value of `$enable_live_network_counts` (passes through). - * - * @note While this routine is attached to a WP filter, we also call upon it directly at times. - */ -$self->updateBlogPaths = function ($enable_live_network_counts = null) use ($self) { - $value = $enable_live_network_counts; // This hook actually rides on a filter. - - if (!$self->options['enable']) { - return $value; // Nothing to do. - } - if (!is_multisite()) { - return $value; // N/A. - } - $cache_dir = $self->cacheDir(); - $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths'; - - $cache_lock = $self->cacheLock(); - - if (!is_dir($cache_dir)) { - mkdir($cache_dir, 0775, true); - } - if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) { - file_put_contents($cache_dir.'/.htaccess', $self->htaccess_deny); - } - if (is_dir($cache_dir) && is_writable($cache_dir)) { - $paths = // Collect child `[/base]/path/`s from the WordPress database. - $self->wpdb()->get_col('SELECT `path` FROM `'.esc_sql($self->wpdb()->blogs)."` WHERE `deleted` <= '0'"); - - $host_base_token = $self->hostBaseToken(); // Pull this once only. - - foreach ($paths as $_key => &$_path) { - if ($_path && $_path !== '/' && $host_base_token && $host_base_token !== '/') { - // Note that each `path` in the DB looks like: `[/base]/path/` (i.e., it includes base). - $_path = '/'.ltrim(preg_replace('/^'.preg_quote($host_base_token, '/').'/', '', $_path), '/'); - } - if (!$_path || $_path === '/') { - unset($paths[$_key]); // Exclude main site. - } - } - unset($_key, $_path); // Housekeeping. - - file_put_contents($blog_paths_file, serialize($paths)); - } - $self->cacheUnlock($cache_lock); // Release. - - return $value; // Pass through untouched (always). -}; - -/* - * Deletes base directory. - * - * @since 151002 Improving multisite compat. - * - * @return int Total files removed by this routine (if any). - */ -$self->deleteBaseDir = function () use ($self) { - $counter = 0; // Initialize. - - @set_time_limit(1800); // @TODO Display a warning. - - $counter += $self->deleteAllFilesDirsIn($self->wpContentBaseDirTo(''), true); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/MenuPageUtils.php b/src/includes/closures/Plugin/MenuPageUtils.php deleted file mode 100644 index 80e75f0..0000000 --- a/src/includes/closures/Plugin/MenuPageUtils.php +++ /dev/null @@ -1,219 +0,0 @@ -enqueueAdminStyles = function () use ($self) { - if (empty($_GET['page']) || strpos($_GET['page'], GLOBAL_NS) !== 0) { - return; // NOT a plugin page in the administrative area. - } - $deps = array(); // Plugin dependencies. - - wp_enqueue_style(GLOBAL_NS, $self->url('/src/client-s/css/menu-pages.min.css'), $deps, VERSION, 'all'); -}; - -/* - * Adds JS for administrative menu pages. - * - * @since 150422 Rewrite. - * - * @attaches-to `admin_enqueue_scripts` hook. - */ -$self->enqueueAdminScripts = function () use ($self) { - if (empty($_GET['page']) || strpos($_GET['page'], GLOBAL_NS) !== 0) { - return; // NOT a plugin page in the administrative area. - } - $deps = array('jquery', 'chartjs'); // Plugin dependencies. - - wp_enqueue_script('chartjs', set_url_scheme('//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js'), array(), null, true); - wp_enqueue_script(GLOBAL_NS, $self->url('/src/client-s/js/menu-pages.min.js'), $deps, VERSION, true); - wp_localize_script(GLOBAL_NS, GLOBAL_NS.'_menu_page_vars', array( - '_wpnonce' => wp_create_nonce(), - 'isMultisite' => is_multisite(), // Network? - 'currentUserHasCap' => current_user_can($self->cap), - 'currentUserHasNetworkCap' => current_user_can($self->network_cap), - 'htmlCompressorEnabled' => (boolean) $self->options['htmlc_enable'], - 'ajaxURL' => site_url('/wp-load.php', is_ssl() ? 'https' : 'http'), - 'emptyStatsCountsImageUrl' => $self->url('/src/client-s/images/stats-fc-empty.png'), - 'emptyStatsFilesImageUrl' => $self->url('/src/client-s/images/stats-fs-empty.png'), - 'i18n' => array( - 'name' => NAME, - 'perSymbol' => __('%', 'comet-cache'), - 'file' => __('file', 'comet-cache'), - 'files' => __('files', 'comet-cache'), - 'pageCache' => __('Page Cache', 'comet-cache'), - 'htmlCompressor' => __('HTML Compressor', 'comet-cache'), - 'currentTotal' => __('Current Total', 'comet-cache'), - 'currentSite' => __('Current Site', 'comet-cache'), - 'xDayHigh' => __('%s Day High', 'comet-cache'), - ), - )); -}; - -/* - * Creates network admin menu pages. - * - * @since 150422 Rewrite. - * - * @attaches-to `network_admin_menu` hook. - */ -$self->addNetworkMenuPages = function () use ($self) { - if (!is_multisite()) { - return; // Not applicable. - } - $icon = file_get_contents(dirname(dirname(dirname(dirname(__FILE__)))).'/client-s/images/inline-icon.svg'); - $icon = 'data:image/svg+xml;base64,'.base64_encode($self->colorSvgMenuIcon($icon)); - - add_menu_page(NAME . (IS_PRO ? ' Pro' : ''), NAME . (IS_PRO ? ' Pro' : ''), $self->network_cap, GLOBAL_NS, array($self, 'menuPageOptions'), $icon); - add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $self->network_cap, GLOBAL_NS, array($self, 'menuPageOptions')); - - - - -}; - -/* - * Creates admin menu pages. - * - * @since 150422 Rewrite. - * - * @attaches-to `admin_menu` hook. - */ -$self->addMenuPages = function () use ($self) { - if (is_multisite()) { - return; // Multisite networks MUST use network admin area. - } - $icon = file_get_contents(dirname(dirname(dirname(dirname(__FILE__)))).'/client-s/images/inline-icon.svg'); - $icon = 'data:image/svg+xml;base64,'.base64_encode($self->colorSvgMenuIcon($icon)); - - add_menu_page(NAME . (IS_PRO ? ' Pro' : ''), NAME . (IS_PRO ? ' Pro' : ''), $self->cap, GLOBAL_NS, array($self, 'menuPageOptions'), $icon); - add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $self->cap, GLOBAL_NS, array($self, 'menuPageOptions')); - - - - -}; - -/* - * Adds link(s) to Comet Cache row on the WP plugins page. - * - * @since 150422 Rewrite. - * - * @attaches-to `plugin_action_links_'.plugin_basename(PLUGIN_FILE)` filter. - * - * @param array $links An array of the existing links provided by WordPress. - * - * @return array Revised array of links. - */ -$self->addSettingsLink = function ($links) use ($self) { - if (is_multisite() && !is_network_admin()) { - return $links; - } - - $links[] = ''.__('Settings', 'comet-cache').''; - if (!IS_PRO) { - $links[] = '
'.__('Preview Pro Features', 'comet-cache').''; - $links[] = ''.__('Upgrade', 'comet-cache').''; - } - return $links; -}; - -/* - * Fills menu page inline SVG icon color. - * - * @since 150422 Rewrite. - * - * @param string $svg Inline SVG icon markup. - * - * @return string Inline SVG icon markup. - */ -$self->colorSvgMenuIcon = function ($svg) use ($self) { - if (!($color = get_user_option('admin_color'))) { - $color = 'fresh'; // Default color scheme. - } - if (empty($self->wp_admin_icon_colors[$color])) { - return $svg; // Not possible. - } - $icon_colors = $self->wp_admin_icon_colors[$color]; - $use_icon_fill_color = $icon_colors['base']; // Default base. - - $current_pagenow = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : ''; - $current_page = !empty($_REQUEST['page']) ? $_REQUEST['page'] : ''; - - if (strpos($current_pagenow, GLOBAL_NS) === 0 || strpos($current_page, GLOBAL_NS) === 0) { - $use_icon_fill_color = $icon_colors['current']; - } - return str_replace(' fill="currentColor"', ' fill="'.esc_attr($use_icon_fill_color).'"', $svg); -}; - -/* - * Loads the admin menu page options. - * - * @since 150422 Rewrite. - */ -$self->menuPageOptions = function () use ($self) { - new MenuPage('options'); -}; - - - - - -/* - * WordPress admin icon color schemes. - * - * @since 150422 Rewrite. - * - * @type array WP admin icon colors. - * - * @note These must be hard-coded, because they don't become available - * in core until `admin_init`; i.e., too late for `admin_menu`. - */ -$self->wp_admin_icon_colors = array( - 'fresh' => array('base' => '#999999', 'focus' => '#2EA2CC', 'current' => '#FFFFFF'), - 'light' => array('base' => '#999999', 'focus' => '#CCCCCC', 'current' => '#CCCCCC'), - 'blue' => array('base' => '#E5F8FF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), - 'midnight' => array('base' => '#F1F2F3', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), - 'sunrise' => array('base' => '#F3F1F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), - 'ectoplasm' => array('base' => '#ECE6F6', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), - 'ocean' => array('base' => '#F2FCFF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), - 'coffee' => array('base' => '#F3F2F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'), -); - -/* - * On a specific menu page? - * - * @since 151002 Improving multisite compat. - * - * @param string $which Which page to check; may contain wildcards. - * - * @return boolean True if is the menu page. - */ -$self->isMenuPage = function ($which) use ($self) { - if (!($which = trim((string) $which))) { - return false; // Empty. - } - if (!is_admin()) { - return false; - } - $page = $pagenow = ''; // Initialize. - - if (!empty($_REQUEST['page'])) { - $page = (string) $_REQUEST['page']; - } - if (!empty($GLOBALS['pagenow'])) { - $pagenow = (string) $GLOBALS['pagenow']; - } - if ($page && fnmatch($which, $page, FNM_CASEFOLD)) { - return true; // Wildcard match. - } - if ($pagenow && fnmatch($which, $pagenow, FNM_CASEFOLD)) { - return true; // Wildcard match. - } - return false; // Nope. -}; diff --git a/src/includes/closures/Plugin/NoticeUtils.php b/src/includes/closures/Plugin/NoticeUtils.php deleted file mode 100644 index f6ebf3b..0000000 --- a/src/includes/closures/Plugin/NoticeUtils.php +++ /dev/null @@ -1,296 +0,0 @@ -enqueueNotice = function ($notice, array $args = array(), $blog_id = 0) use ($self) { - $notice = trim((string) $notice); - $blog_id = (integer) $blog_id; - - if (!$notice) { - return; // Nothing to do. - } - $notice = array('notice' => $notice); - $notice = $self->normalizeNotice($notice, $args); - $key = sha1(serialize($notice)); // Prevent dupes. - - $notices = $self->getNotices($blog_id); - - if ($notice['push_to_top']) { - $notices = array($key => $notice) + $notices; - } else { - $notices[$key] = $notice; // Default behavior. - } - $self->updateNotices($notices, $blog_id); - - return $key; // For dismissals. -}; - -/* - * Dismiss an administrative notice. - * - * @since 151002 Improving multisite compat. - * - * @param string $key_to_dismiss A unique key which identifies a particular notice. - * Or, a persistent key which identifies one or more persistent notices. - * - * @param integer $blog_id The blog ID from which to dismiss the notice. - * - * @return array All remaining notices. - */ -$self->dismissNotice = function ($key_to_dismiss, $blog_id = 0) use ($self) { - $key_to_dismiss = trim((string) $key_to_dismiss); - $blog_id = (integer) $blog_id; // For multisite compat. - $notices = $enqueued_notices = $self->getNotices($blog_id); - - if (!$key_to_dismiss) { - return $notices; // Nothing to do. - } - foreach ($notices as $_key => $_notice) { - if ($_key === $key_to_dismiss) { - unset($notices[$_key]); // A specific key. - } elseif ($_notice['persistent_key'] === $key_to_dismiss) { - unset($notices[$_key]); // All matching keys. - } - } // ↑ Dismisses all matching keys. - unset($_key, $_notice); // Housekeeping. - - if ($notices !== $enqueued_notices) { // Something changed? - $self->updateNotices($notices, $blog_id); // Update. - } - return $notices; // All remaining notices. -}; - -/* - * Enqueue an administrative error notice. - * - * @since 150422 Rewrite. Improved 151002. - */ -$self->enqueueError = function ($notice, array $args = array(), $blog_id = 0) use ($self) { - return $self->enqueueNotice($notice, array_merge($args, array('class' => 'error')), $blog_id); -}; - -/* - * Enqueue an administrative notice (main site). - * - * @since 151002. Improving multisite compat. - */ -$self->enqueueMainNotice = function ($notice, array $args = array()) use ($self) { - return $self->enqueueNotice($notice, $args, -1); -}; - -/* - * Enqueue an administrative error notice (main site). - * - * @since 151002. Improving multisite compat. - */ -$self->enqueueMainError = function ($notice, array $args = array()) use ($self) { - return $self->enqueueNotice($notice, array_merge($args, array('class' => 'error')), -1); -}; - -/* - * Dismiss an administrative notice (main site). - * - * @since 151002 Improving multisite compat. - */ -$self->dismissMainNotice = function ($key_to_dismiss) use ($self) { - return $self->dismissNotice($key_to_dismiss, -1); -}; - -/* - * Notice display handler. - */ - -/* - * Render admin notices. - * - * @since 150422 Rewrite. Improved 151002. - * - * @attaches-to `all_admin_notices` hook. - */ -$self->allAdminNotices = function () use ($self) { - $notices = $enqueued_notices = $self->getNotices(); - - foreach ($notices as $_key => $_notice) { - # Always dismiss all non-persistent transients. - - if ($_notice['is_transient'] && !$_notice['persistent_key']) { - unset($notices[$_key]); // Dismiss. - } - # Current user can see this notice? - - if (!current_user_can($self->cap)) { - continue; // Current user unable to see. - } - if ($_notice['cap_required'] && !current_user_can($_notice['cap_required'])) { - continue; // Current user unable to see this notice. - } - # Current URI matches a limited scope/context for this notice? - - if ($_notice['only_on_uris'] && !@preg_match($_notice['only_on_uris'], $_SERVER['REQUEST_URI'])) { - continue; // Not in the right context at the moment; i.e., does not regex. - } - # If persistent, allow a site owner to dismiss. - - $_dismiss = ''; // Reset this to its default state. - if ($_notice['persistent_key'] && $_notice['dismissable']) { // See above. The `dismissNotice()` action requires `$self->cap` always. - $_dismiss = add_query_arg(urlencode_deep(array(GLOBAL_NS => array('dismissNotice' => array('key' => $_key)), '_wpnonce' => wp_create_nonce()))); - $_dismiss = ''.__('dismiss ×', 'comet-cache').''; - } - # Display this notice. If not persistent, we can dismiss it too. - - echo '

'.$_notice['notice'].$_dismiss.'

'; - - if (!$_notice['persistent_key']) { // If not persistent, dismiss. - unset($notices[$_key]); // Dismiss; this notice has been displayed now. - } - } - unset($_key, $_notice, $_dismiss); // Housekeeping. - - # Update notices if something changed above. - - if ($notices !== $enqueued_notices) { // Something changed? - $self->updateNotices($notices); // Update. - } -}; - -/* - * Notice getter/setter. - */ - -/* - * Get admin notices. - * - * @since 151002 Improving multisite compat. - * - * @param integer $blog_id Optional. Defaults to the current blog ID. - * Use any value `< 0` to indicate the main site. - * - * @return array All notices. - */ -$self->getNotices = function ($blog_id = 0) use ($self) { - if (is_multisite()) { - if (!($blog_id = (integer) $blog_id)) { - $blog_id = (integer) get_current_blog_id(); - } - if ($blog_id < 0) { // Blog for main site. - $blog_id = (integer) get_current_site()->blog_id; - } - $blog_suffix = '_'.$blog_id; // Site option suffix. - $notices = get_site_option(GLOBAL_NS.$blog_suffix.'_notices'); - } else { - $notices = get_site_option(GLOBAL_NS.'_notices'); - } - if (!is_array($notices)) { - $notices = array(); // Force array. - // Prevent multiple DB queries by adding this key. - $self->updateNotices($notices, $blog_id); - } - foreach ($notices as $_key => &$_notice) { - if (!is_string($_key) || !is_array($_notice) || empty($_notice['notice'])) { - unset($notices[$_key]); // Old notice. - continue; // Bypass; i.e., do not normalize. - } - $_notice = $self->normalizeNotice($_notice); - } // ↑ Typecast/normalized each of the array elements. - unset($_key, $_notice); // Housekeeping. - - return $notices; -}; - -/* - * Update admin notices. - * - * @since 151002 Improving multisite compat. - * - * @param array $notices New array of notices. - * - * @param integer $blog_id Optional. Defaults to the current blog ID. - * Use any value `< 0` to indicate the main site. - * - * @return array All notices. - */ -$self->updateNotices = function (array $notices, $blog_id = 0) use ($self) { - if (is_multisite()) { - if (!($blog_id = (integer) $blog_id)) { - $blog_id = (integer) get_current_blog_id(); - } - if ($blog_id < 0) { // Blog for main site. - $blog_id = (integer) get_current_site()->blog_id; - } - $blog_suffix = '_'.$blog_id; // Site option suffix. - update_site_option(GLOBAL_NS.$blog_suffix.'_notices', $notices); - } else { - update_site_option(GLOBAL_NS.'_notices', $notices); - } - return $notices; -}; - -/* - * Notice property utilities. - */ - -/* -* Normalize notice elements. -* -* @since 151002 Improving multisite compat. -* -* @param array $notice Notice array elements. -* @param array $args Any additional array elements. -* -* @return array Normalized notice array elements. -*/ -$self->normalizeNotice = function (array $notice, array $args = array()) use ($self) { - $notice_defaults = array( - 'notice' => '', - 'only_on_uris' => '', - 'persistent_key' => '', - 'dismissable' => true, - 'is_transient' => true, - 'push_to_top' => false, - 'class' => 'updated', - 'cap_required' => '', // `$self->cap` always. - // i.e., this cap is in addition to `$self->cap`. - ); - $notice = array_merge($notice_defaults, $notice, $args); - $notice = array_intersect_key($notice, $notice_defaults); - - foreach ($notice as $_key => &$_value) { - switch ($_key) { - case 'notice': - case 'only_on_uris': - case 'persistent_key': - $_value = trim((string) $_value); - break; // Stop here. - - case 'is_transient': - case 'push_to_top': - case 'dismissable': - $_value = (boolean) $_value; - break; // Stop here. - - case 'class': - case 'cap_required': - $_value = trim((string) $_value); - break; // Stop here. - } - } // ↑ Typecast each of the array elements. - unset($_key, $_value); // A little housekeeping. - - ksort($notice); // For more accurate comparison in other routines. - - return $notice; // Normalized. -}; diff --git a/src/includes/closures/Plugin/OptionUtils.php b/src/includes/closures/Plugin/OptionUtils.php deleted file mode 100644 index faae1b1..0000000 --- a/src/includes/closures/Plugin/OptionUtils.php +++ /dev/null @@ -1,70 +0,0 @@ -getOptions = function () use ($self) { - if (!($options = $self->options)) { // Not defined yet? - if (!is_array($options = get_site_option(GLOBAL_NS.'_options'))) { - $options = array(); // Force array. - } - if (!$options && is_array($zencache_options = get_site_option('zencache_options'))) { - $options = $zencache_options; // Old ZenCache options. - $options['crons_setup'] = $this->default_options['crons_setup']; - } - } - $self->options = array_merge($self->default_options, $options); - $self->options = $self->applyWpFilters(GLOBAL_NS.'_options', $self->options); - $self->options = array_intersect_key($self->options, $self->default_options); - - foreach ($self->options as $_key => &$_value) { - $_value = trim((string) $_value); // Force strings. - } unset($_key, $_value); // Housekeeping. - - $self->options['base_dir'] = trim($self->options['base_dir'], '\\/'." \t\n\r\0\x0B"); - if (!$self->options['base_dir'] || strpos(basename($self->options['base_dir']), 'wp-') === 0) { - $self->options['base_dir'] = $self->default_options['base_dir']; - } - return $self->options; // Plugin options. -}; - -/* - * Update plugin options. - * - * @since 151002 Improving multisite compat. - * - * @param array $options One or more new options. - * - * @return array Plugin options after update. - */ -$self->updateOptions = function (array $options) use ($self) { - if (!IS_PRO) { // Do not save Pro option keys. - $options = array_diff_key($options, $self->pro_only_option_keys); - } - if (!empty($options['base_dir']) && $options['base_dir'] !== $self->options['base_dir']) { - $self->tryErasingAllFilesDirsIn($self->wpContentBaseDirTo('')); - } - $self->options = array_merge($self->default_options, $self->options, $options); - $self->options = array_intersect_key($self->options, $self->default_options); - update_site_option(GLOBAL_NS.'_options', $self->options); - - return $self->getOptions(); -}; - -/* - * Restore default plugin options. - * - * @since 151002 Improving multisite compat. - * - * @return array Plugin options after update. - */ -$self->restoreDefaultOptions = function () use ($self) { - delete_site_option(GLOBAL_NS.'_options'); // Force restore. - $self->options = $self->default_options; // In real-time. - return $self->getOptions(); -}; diff --git a/src/includes/closures/Plugin/PostUtils.php b/src/includes/closures/Plugin/PostUtils.php deleted file mode 100644 index 8d92462..0000000 --- a/src/includes/closures/Plugin/PostUtils.php +++ /dev/null @@ -1,42 +0,0 @@ -postStatuses = function () use ($self) { - if (!is_null($statuses = &$self->cacheKey('postStatuses'))) { - return $statuses; // Already did this. - } - $statuses = get_post_stati(); - $statuses = array_keys($statuses); - - return $statuses; -}; - -/* - * All built-in post statuses. - * - * @since 150821 Improving bbPress support. - * - * @return array All built-in post statuses. - */ -$self->builtInPostStatuses = function () use ($self) { - if (!is_null($statuses = &$self->cacheKey('builtInPostStatuses'))) { - return $statuses; // Already did this. - } - $statuses = array(); // Initialize. - - foreach (get_post_stati(array(), 'objects') as $_key => $_status) { - if (!empty($_status->_builtin)) { - $statuses[] = $_status->name; - } - } - unset($_key, $_status); // Housekeeping. - - return $statuses; -}; diff --git a/src/includes/closures/Plugin/UpdateUtils.php b/src/includes/closures/Plugin/UpdateUtils.php deleted file mode 100644 index 6ed814a..0000000 --- a/src/includes/closures/Plugin/UpdateUtils.php +++ /dev/null @@ -1,46 +0,0 @@ -maybeCheckLatestLiteVersion = function () use ($self) { - if (IS_PRO) { - return; // Not applicable. - } - if (!$self->options['lite_update_check']) { - return; // Nothing to do. - } - if (!current_user_can($self->update_cap)) { - return; // Nothing to do. - } - if (is_multisite() && !current_user_can($self->network_cap)) { - return; // Nothing to do. - } - if ($self->options['last_lite_update_check'] >= strtotime('-1 hour')) { - return; // No reason to keep checking on this. - } - $self->updateOptions(array('last_lite_update_check' => time())); - - $product_api_url = 'https://'.urlencode(DOMAIN).'/'; - $product_api_input_vars = array('product_api' => array('action' => 'latest_lite_version')); - - $product_api_response = wp_remote_post($product_api_url, array('body' => $product_api_input_vars)); - $product_api_response = json_decode(wp_remote_retrieve_body($product_api_response)); - - if (is_object($product_api_response) && !empty($product_api_response->lite_version)) { - $self->updateOptions(array('latest_lite_version' => $product_api_response->lite_version)); - } - // Disabling the notice for now. We only run this check to collect the latest version number. - #if ($self->options['latest_lite_version'] && version_compare(VERSION, $self->options['latest_lite_version'], '<')) { - # $self->dismissMainNotice('new-lite-version-available'); // Dismiss any existing notices like this. - # $lite_updater_page = network_admin_url('/plugins.php'); // In a network this points to the master plugins list. - # $self->enqueueMainNotice(sprintf(__('%1$s: a new version is now available. Please upgrade to v%3$s.', 'comet-cache'), esc_html(NAME), esc_attr($lite_updater_page), esc_html($self->options['latest_lite_version'])), array('persistent_key' => 'new-lite-version-available')); - #} -}; - - diff --git a/src/includes/closures/Plugin/UrlUtils.php b/src/includes/closures/Plugin/UrlUtils.php deleted file mode 100644 index 0833a6a..0000000 --- a/src/includes/closures/Plugin/UrlUtils.php +++ /dev/null @@ -1,22 +0,0 @@ -url = function ($file = '', $scheme = '') use ($self) { - $url = rtrim(plugin_dir_url(PLUGIN_FILE), '/'); - $url .= (string) $file; - - if ($scheme) { - $url = set_url_scheme($url, (string) $scheme); - } - return $url; -}; diff --git a/src/includes/closures/Plugin/UserUtils.php b/src/includes/closures/Plugin/UserUtils.php deleted file mode 100644 index 167a99d..0000000 --- a/src/includes/closures/Plugin/UserUtils.php +++ /dev/null @@ -1,118 +0,0 @@ -currentUserCanClearCache = function () use ($self) { - if (!is_null($can = &$self->cacheKey('currentUserCanClearCache'))) { - return $can; // Already cached this. - } - $is_multisite = is_multisite(); - - if (!$is_multisite && current_user_can($self->cap)) { - return ($can = true); // Plugin admin. - } - if ($is_multisite && current_user_can($self->network_cap)) { - return ($can = true); // Plugin admin. - } - - return ($can = false); -}; -$self->currentUserCanWipeCache = $self->currentUserCanClearCache; - -/* - * Current user can clear the opcache? - * - * @since 151114 Enhancing user permissions. - * - * @return boolean Current user can clear the opcache? - */ -$self->currentUserCanClearOpCache = function () use ($self) { - if (!is_null($can = &$self->cacheKey('currentUserCanClearOpCache'))) { - return $can; // Already cached this. - } - $is_multisite = is_multisite(); - - if (!$is_multisite && current_user_can($self->cap)) { - return ($can = true); // Plugin admin. - } - if ($is_multisite && current_user_can($self->network_cap)) { - return ($can = true); // Plugin admin. - } - return ($can = false); -}; -$self->currentUserCanWipeOpCache = $self->currentUserCanClearOpCache; - -/* - * Current user can clear the CDN cache? - * - * @since 151114 Enhancing user permissions. - * - * @return boolean Current user can clear the CDN cache? - */ -$self->currentUserCanClearCdnCache = function () use ($self) { - if (!is_null($can = &$self->cacheKey('currentUserCanClearCdnCache'))) { - return $can; // Already cached this. - } - $is_multisite = is_multisite(); - - if (!$is_multisite && current_user_can($self->cap)) { - return ($can = true); // Plugin admin. - } - if ($is_multisite && current_user_can($self->network_cap)) { - return ($can = true); // Plugin admin. - } - return ($can = false); -}; -$self->currentUserCanWipeCdnCache = $self->currentUserCanClearCdnCache; - -/* -* Current user can clear expired transients? -* -* @since 151220 Enhancing user permissions. -* -* @return boolean Current user can clear expired transients? -*/ -$self->currentUserCanClearExpiredTransients = function () use ($self) { - if (!is_null($can = &$self->cacheKey('currentUserCanClearExpiredTransients'))) { - return $can; // Already cached this. - } - $is_multisite = is_multisite(); - - if (!$is_multisite && current_user_can($self->cap)) { - return ($can = true); // Plugin admin. - } - if ($is_multisite && current_user_can($self->network_cap)) { - return ($can = true); // Plugin admin. - } - return ($can = false); -}; -$self->currentUserCanWipeExpiredTransients = $self->currentUserCanClearExpiredTransients; - -/* - * Current user can see stats? - * - * @since 151002 Enhancing user permissions. - * - * @return boolean Current user can see stats? - */ -$self->currentUserCanSeeStats = function () use ($self) { - if (!is_null($can = &$self->cacheKey('currentUserCanSeeStats'))) { - return $can; // Already cached this. - } - $is_multisite = is_multisite(); - - if (!$is_multisite && current_user_can($self->cap)) { - return ($can = true); // Plugin admin. - } - if ($is_multisite && current_user_can($self->network_cap)) { - return ($can = true); // Plugin admin. - } - - return ($can = false); -}; diff --git a/src/includes/closures/Plugin/WcpAuthorUtils.php b/src/includes/closures/Plugin/WcpAuthorUtils.php deleted file mode 100644 index c43b6df..0000000 --- a/src/includes/closures/Plugin/WcpAuthorUtils.php +++ /dev/null @@ -1,92 +0,0 @@ -autoClearAuthorPageCache = function ($post_id, \WP_Post $post_after, \WP_Post $post_before) use ($self) { - $counter = 0; // Initialize. - $enqueued_notices = 0; // Initialize. - $authors = array(); // Initialize. - $authors_to_clear = array(); // Initialize. - - if (!($post_id = (integer) $post_id)) { - return $counter; // Nothing to do. - } - if (!is_null($done = &$self->cacheKey('autoClearAuthorPageCache', array($post_id, $post_after->ID, $post_before->ID)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_author_page_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - /* - * If we're changing the post author AND - * the previous post status was either 'published' or 'private' - * then clear the author page for both authors. - * - * Else if the old post status was 'published' or 'private' OR - * the new post status is 'published' or 'private' - * then clear the author page for the current author. - * - * Else return the counter; post status does not warrant clearing author page cache. - */ - if ($post_after->post_author !== $post_before->post_author && - ($post_before->post_status === 'publish' || $post_before->post_status === 'private') - ) { - $authors[] = (integer) $post_before->post_author; - $authors[] = (integer) $post_after->post_author; - } elseif (($post_before->post_status === 'publish' || $post_before->post_status === 'private') || - ($post_after->post_status === 'publish' || $post_after->post_status === 'private') - ) { - $authors[] = (integer) $post_after->post_author; - } - if (!$authors) { - return $counter; // Nothing to do. - } - foreach ($authors as $_author_id) { - $authors_to_clear[$_author_id]['posts_url'] = get_author_posts_url($_author_id); - $authors_to_clear[$_author_id]['display_name'] = get_the_author_meta('display_name', $_author_id); - } - unset($_author_id); // Housekeeping. - - foreach ($authors_to_clear as $_author) { - $_author_regex = $self->buildHostCachePathRegex($_author['posts_url']); - $_author_counter = $self->clearFilesFromHostCacheDir($_author_regex); - $counter += $_author_counter; // Add to overall counter. - - if ($_author_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for Author Page: %3$s; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($_author_counter)), esc_html($_author['display_name']))); - $enqueued_notices++; // Increment enqueued notices counter. - } - } - unset($_author, $_author_regex, $_author_counter); // Housekeeping. - - $counter += $self->autoClearXmlFeedsCache('blog'); - $counter += $self->autoClearXmlFeedsCache('post-authors', $post_id); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpCommentUtils.php b/src/includes/closures/Plugin/WcpCommentUtils.php deleted file mode 100644 index 03ee6f4..0000000 --- a/src/includes/closures/Plugin/WcpCommentUtils.php +++ /dev/null @@ -1,96 +0,0 @@ -autoClearCommentPostCache = function ($comment_id) use ($self) { - $counter = 0; // Initialize. - - if (!($comment_id = (integer) $comment_id)) { - return $counter; // Nothing to do. - } - if (!is_null($done = &$self->cacheKey('autoClearCommentPostCache', $comment_id))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!is_object($comment = get_comment($comment_id))) { - return $counter; // Nothing we can do. - } - if (empty($comment->comment_post_ID)) { - return $counter; // Nothing we can do. - } - if ($comment->comment_approved === 'spam' || $comment->comment_approved === '0') { - // Don't allow next `autoClearPostCache()` call to clear post cache. - $allow = &$self->cacheKey('autoClearPostCache_allow'); - $allow = false; // Flag as false; i.e., disallow. - return $counter; - } - $counter += $self->autoClearXmlFeedsCache('blog-comments'); - $counter += $self->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID); - $counter += $self->autoClearPostCache($comment->comment_post_ID); - - return $counter; -}; - -/* - * Automatically clears cache files for a post associated with a particular comment. - * - * @since 150422 Rewrite. - * - * @attaches-to `transition_comment_status` hook. - * - * @param string $new_status New comment status. - * @param string $old_status Old comment status. - * @param \stdClass $comment Comment object. - * - * @throws \Exception If a clear failure occurs. - * - * @return int Total files cleared by this routine (if any). - * - * @note This is also called upon by other routines which listen for - * events that are indirectly associated with a comment ID. - */ -$self->autoClearCommentPostCacheTransition = function ($new_status, $old_status, $comment) use ($self) { - $counter = 0; // Initialize. - - if (!is_object($comment)) { - return $counter; // Nothing we can do. - } - if (empty($comment->comment_post_ID)) { - return $counter; // Nothing we can do. - } - if (!is_null($done = &$self->cacheKey('autoClearCommentPostCacheTransition', array($new_status, $old_status, $comment->comment_post_ID)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!($old_status === 'approved' || ($old_status === 'unapproved' && $new_status === 'approved'))) { - // If excluded here, don't allow next `autoClearPostCache()` call to clear post cache. - $allow = &$self->cacheKey('autoClearPostCache_allow'); - $allow = false; // Flag as false; i.e., disallow. - return $counter; - } - $counter += $self->autoClearXmlFeedsCache('blog-comments'); - $counter += $self->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID); - $counter += $self->autoClearPostCache($comment->comment_post_ID); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpFeedUtils.php b/src/includes/closures/Plugin/WcpFeedUtils.php deleted file mode 100644 index e1e39a9..0000000 --- a/src/includes/closures/Plugin/WcpFeedUtils.php +++ /dev/null @@ -1,116 +0,0 @@ -autoClearXmlFeedsCache = function ($type, $post_id = 0) use ($self) { - $counter = 0; // Initialize. - - if (!($type = (string) $type)) { - return $counter; // Nothing we can do. - } - $post_id = (integer) $post_id; // Force integer. - - if (!is_null($done = &$self->cacheKey('autoClearXmlFeedsCache', array($type, $post_id)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['feeds_enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_xml_feeds_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $utils = new FeedUtils(); // Feed utilities. - $variations = $variation_regex_frags = array(); // Initialize. - - switch ($type) { // Handle clearing based on the `$type`. - - case 'blog': // The blog feed; i.e. `/feed/` on most WP installs. - $variations = array_merge($variations, $utils->feedLinkVariations()); - break; // Break switch handler. - - case 'blog-comments': // The blog comments feed; i.e. `/comments/feed/` on most WP installs. - $variations = array_merge($variations, $utils->feedLinkVariations('comments_')); - break; // Break switch handler. - - case 'post-comments': // Feeds related to comments that a post has. - if (!$post_id) { - break; // Break switch handler. - } - if (!($post = get_post($post_id))) { - break; // Break switch handler. - } - $variations = array_merge($variations, $utils->postCommentsFeedLinkVariations($post)); - break; // Break switch handler. - - case 'post-authors': // Feeds related to authors that a post has. - if (!$post_id) { - break; // Break switch handler. - } - if (!($post = get_post($post_id))) { - break; // Break switch handler. - } - $variations = array_merge($variations, $utils->postAuthorFeedLinkVariations($post)); - break; // Break switch handler. - - case 'post-terms': // Feeds related to terms that a post has. - if (!$post_id) { - break; // Break switch handler. - } - if (!($post = get_post($post_id))) { - break; // Break switch handler. - } - $variations = array_merge($variations, $utils->postTermFeedLinkVariations($post, true)); - break; // Break switch handler. - - case 'custom-post-type': // Feeds related to a custom post type archive view. - if (!$post_id) { - break; // Break switch handler. - } - if (!($post = get_post($post_id))) { - break; // Break switch handler. - } - $variations = array_merge($variations, $utils->postTypeArchiveFeedLinkVariations($post)); - break; // Break switch handler. - - // @TODO Possibly consider search-related feeds in the future. - // See: - } - if (!($variation_regex_frags = $utils->convertVariationsToHostCachePathRegexFrags($variations))) { - return $counter; // Nothing to do here. - } - $in_sets_of = $self->applyWpFilters(GLOBAL_NS.'_autoClearXmlFeedsCache_in_sets_of', 10, get_defined_vars()); - for ($_i = 0; $_i < count($variation_regex_frags); $_i = $_i + $in_sets_of) { - $_variation_regex_frags = array_slice($variation_regex_frags, $_i, $in_sets_of); - $_regex = '/^\/(?:'.implode('|', $_variation_regex_frags).')\./i'; - $counter += $self->clearFilesFromHostCacheDir($_regex); - } - unset($_i, $_variation_regex_frags, $_regex); // Housekeeping. - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache, for XML feeds of type: %3$s; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($type))); - } - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpHomeBlogUtils.php b/src/includes/closures/Plugin/WcpHomeBlogUtils.php deleted file mode 100644 index 9cad0f2..0000000 --- a/src/includes/closures/Plugin/WcpHomeBlogUtils.php +++ /dev/null @@ -1,101 +0,0 @@ -autoClearHomePageCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearHomePageCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_home_page_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $regex = $self->buildHostCachePathRegex(home_url('/')); - $counter += $self->clearFilesFromHostCacheDir($regex); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for the designated "Home Page"; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - $counter += $self->autoClearXmlFeedsCache('blog'); - - return $counter; -}; - -/* - * Automatically clears cache files for the posts page. - * - * @since 150422 Rewrite. - * - * @throws \Exception If a clear failure occurs. - * - * @return int Total files cleared by this routine (if any). - * - * @note Unlike many of the other `auto_` methods, this one is NOT currently - * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}. - */ -$self->autoClearPostsPageCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearPostsPageCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_posts_page_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $show_on_front = get_option('show_on_front'); - $page_for_posts = get_option('page_for_posts'); - - if (!in_array($show_on_front, array('posts', 'page'), true)) { - return $counter; // Nothing we can do in this case. - } - if ($show_on_front === 'page' && !$page_for_posts) { - return $counter; // Nothing we can do. - } - if ($show_on_front === 'posts') { - $posts_page = home_url('/'); - } elseif ($show_on_front === 'page') { - $posts_page = get_permalink($page_for_posts); - } - if (empty($posts_page)) { - return $counter; // Nothing we can do. - } - $regex = $self->buildHostCachePathRegex($posts_page); - $counter += $self->clearFilesFromHostCacheDir($regex); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for the designated "Posts Page"; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - $counter += $self->autoClearXmlFeedsCache('blog'); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpJetpackUtils.php b/src/includes/closures/Plugin/WcpJetpackUtils.php deleted file mode 100644 index 0cd6bec..0000000 --- a/src/includes/closures/Plugin/WcpJetpackUtils.php +++ /dev/null @@ -1,24 +0,0 @@ -autoClearCacheOnJetpackCustomCss = function ($args) use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearCacheOnJetpackCustomCss', $args))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (empty($args['is_preview']) && class_exists('\\Jetpack')) { - $counter += $self->autoClearCache(); - } -}; diff --git a/src/includes/closures/Plugin/WcpOpcacheUtils.php b/src/includes/closures/Plugin/WcpOpcacheUtils.php deleted file mode 100644 index 1137aa4..0000000 --- a/src/includes/closures/Plugin/WcpOpcacheUtils.php +++ /dev/null @@ -1,69 +0,0 @@ -wipeOpcache = function ($manually = false, $maybe = true, $files = array()) use ($self) { - $counter = 0; // Initialize counter. - - if ($maybe && !$self->options['cache_clear_opcache_enable']) { - return $counter; // Not enabled at this time. - } - if (!$self->functionIsPossible('opcache_reset')) { - return $counter; // Not possible. - } - if (!($status = $self->sysOpcacheStatus())) { - return $counter; // Not possible. - } - if (empty($status->opcache_enabled)) { - return $counter; // Not necessary. - } - if (empty($status->opcache_statistics->num_cached_keys)) { - return $counter; // Not possible. - } - if ($files) { // Specific files? - foreach ($files as $_file) { - $counter += (int) opcache_invalidate($_file, true); - } // unset($_file); // Housekeeping. - } elseif (opcache_reset()) { // True if a reset occurs. - $counter += $status->opcache_statistics->num_cached_keys; - } - return $counter; -}; - -/* - * Clear (i.e., reset) OPCache. - * - * @since 151002 Adding OPCache support. - * - * @param bool $manually True if clearing is done manually. - * @param boolean $maybe Defaults to a true value. - * - * @return integer Total keys cleared. - */ -$self->clearOpcache = function ($manually = false, $maybe = true) use ($self) { - if (!is_multisite() || is_main_site() || current_user_can($self->network_cap)) { - return $self->wipeOpcache($manually, $maybe); - } - return 0; // Not applicable. -}; - -/* - * Clear AC class file from Opcache (by force). - * - * @since 151215 Adding OPCache support. - * - * @return integer Total keys cleared. - */ -$self->clearAcDropinFromOpcacheByForce = function () use ($self) { - return $self->wipeOpcache(false, false, array(WP_CONTENT_DIR.'/advanced-cache.php')); -}; diff --git a/src/includes/closures/Plugin/WcpPluginUtils.php b/src/includes/closures/Plugin/WcpPluginUtils.php deleted file mode 100644 index 41f3d3b..0000000 --- a/src/includes/closures/Plugin/WcpPluginUtils.php +++ /dev/null @@ -1,22 +0,0 @@ -autoClearOnPluginActivationDeactivation = function ($plugin, $network_wide = false) use ($self) { - if (!$self->applyWpFilters(GLOBAL_NS.'_auto_clear_on_plugin_activation_deactivation', true)) { - return 0; // Nothing to do here. - } - return $self->{($network_wide ? 'autoWipeCache' : 'autoClearCache')}(); -}; diff --git a/src/includes/closures/Plugin/WcpPostTypeUtils.php b/src/includes/closures/Plugin/WcpPostTypeUtils.php deleted file mode 100644 index 09be6c5..0000000 --- a/src/includes/closures/Plugin/WcpPostTypeUtils.php +++ /dev/null @@ -1,66 +0,0 @@ -autoClearCustomPostTypeArchiveCache = function ($post_id) use ($self) { - $counter = 0; // Initialize. - - if (!($post_id = (integer) $post_id)) { - return $counter; // Nothing to do. - } - if (!is_null($done = &$self->cacheKey('autoClearCustomPostTypeArchiveCache', $post_id))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_custom_post_type_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - if (!($post_type = get_post_type($post_id))) { - return $counter; // Nothing to do. - } - if (!($all_custom_post_types = get_post_types(array('_builtin' => false)))) { - return $counter; // No custom post types. - } - if (!in_array($post_type, array_keys($all_custom_post_types), true)) { - return $counter; // This is NOT a custom post type. - } - if (!($custom_post_type = get_post_type_object($post_type))) { - return $counter; // Unable to retrieve post type. - } - if (empty($custom_post_type->labels->name) || !($custom_post_type_name = $custom_post_type->labels->name)) { - $custom_post_type_name = __('Untitled', 'comet-cache'); - } - if (!($custom_post_type_archive_link = get_post_type_archive_link($post_type))) { - return $counter; // Nothing to do; no link to work from in this case. - } - $regex = $self->buildHostCachePathRegex($custom_post_type_archive_link); - $counter += $self->clearFilesFromHostCacheDir($regex); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for Custom Post Type: %3$s; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($custom_post_type_name))); - } - $counter += $self->autoClearXmlFeedsCache('custom-post-type', $post_id); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpPostUtils.php b/src/includes/closures/Plugin/WcpPostUtils.php deleted file mode 100644 index b7f0ab4..0000000 --- a/src/includes/closures/Plugin/WcpPostUtils.php +++ /dev/null @@ -1,176 +0,0 @@ -autoClearPostCache = function ($post_id, $force = false) use ($self) { - $counter = 0; // Initialize. - - if (!is_null($allow = &$self->cacheKey('autoClearPostCache_allow'))) { - if ($allow === false) { // Disallow? - $allow = true; // Reset flag. - return $counter; - } - } - if (!($post_id = (integer) $post_id)) { - return $counter; // Nothing to do. - } - if (!is_null($done = &$self->cacheKey('autoClearPostCache', array($post_id, $force)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - if (!($post_type = get_post_type($post_id))) { - return $counter; // Nothing to do. - } - $post_statuses = $self->postStatuses(); - $unpublished_post_statuses = array_diff($post_statuses, array('publish')); - $is_bbpress_post_type = in_array($post_type, $self->bbPressPostTypes(), true); - - if (!empty($self->pre_post_update_post_permalink[$post_id])) { - $permalink = $self->pre_post_update_post_permalink[$post_id]; - $self->pre_post_update_post_permalink[$post_id] = ''; // Reset; only used for post status transitions. - } elseif (!($permalink = get_permalink($post_id))) { - return $counter; // Nothing we can do. - } - if (!($post_status = get_post_status($post_id))) { - return $counter; // Nothing to do. - } - if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish']) - && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts') - && strpos(wp_get_referer(), '/post-new.php') !== false - ) { - $post_status = 'publish'; // A new post being published now. - } - if (in_array($post_status, array('inherit', 'auto-draft'), true)) { - return $counter; // Nothing to do. Note: `inherit` = revision. - } - if (in_array($post_status, array('draft', 'pending', 'future', 'trash'), true) && !$force) { - return $counter; // Nothing to do; i.e., NOT forcing in this case. - } - if (($post_type_obj = get_post_type_object($post_type)) && !empty($post_type_obj->labels->singular_name)) { - $post_type_singular_name = $post_type_obj->labels->singular_name; // Singular name for the post type. - } else { - $post_type_singular_name = __('Post', 'comet-cache'); // Default value. - } - $regex = $self->buildHostCachePathRegex($permalink); - $counter += $self->clearFilesFromHostCacheDir($regex); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for %3$s ID: %4$s; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)), esc_html($post_type_singular_name), esc_html($post_id))); - } - $counter += $self->autoClearXmlFeedsCache('blog'); - $counter += $self->autoClearXmlFeedsCache('post-terms', $post_id); - $counter += $self->autoClearXmlFeedsCache('post-authors', $post_id); - - $counter += $self->autoClearXmlSitemapsCache(); - $counter += $self->autoClearHomePageCache(); - $counter += $self->autoClearPostsPageCache(); - $counter += $self->autoClearPostTermsCache($post_id, $force); - $counter += $self->autoClearCustomPostTypeArchiveCache($post_id); - - - if ($post_type !== 'page' && ($parent_post_id = wp_get_post_parent_id($post_id))) { - // Recursion: i.e., nested post types like bbPress forums/topic/replies. - $counter += $self->autoClearPostCache($parent_post_id, $force); - } - return $counter; -}; -$self->auto_clear_post_cache = $self->autoClearPostCache; // Back compat. - -/* - * Handles post status transitioning. - * - * @attaches-to `pre_post_update` hook. - * - * @since 150422 Rewrite. - * - * @param int $post_id Post ID. - * @param array $data Array of unslashed post data. - * - * @throws \Exception If a clear failure occurs. - * - * @return int Total files cleared by this routine (if any). - * - * @note This is also called upon by other routines which listen for - * events that are indirectly associated with a post ID. - */ -$self->autoClearPostCacheTransition = function ($post_id, $data) use ($self) { - $counter = 0; // Initialize. - - $old_status = (string) get_post_status($post_id); - $new_status = (string) $data['post_status']; - /* - * When a post has a status of `pending` or `draft`, the `get_permalink()` function - * does not return a friendly permalink and therefore `autoClearPostCache()` will - * have no way of building a path to the cache file that should be cleared as part of - * this post status transition. To get around this, we temporarily store the permalink - * in $self->pre_post_update_post_permalink for `autoClearPostCache()` to use. - * - * See also: - */ - if (in_array($new_status, array('pending', 'draft'), true)) { - $self->pre_post_update_post_permalink[$post_id] = get_permalink($post_id); - } - // Begin post status transition sub-routine now. - - if (!is_null($done = &$self->cacheKey('autoClearPostCacheTransition', array($old_status, $new_status, $post_id)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if ($new_status === $old_status) { - return $counter; // Nothing to do. - } - if (!($post_type = get_post_type($post_id))) { - return $counter; // Nothing to do. - } - $post_statuses = $self->postStatuses(); - $unpublished_post_statuses = array_diff($post_statuses, array('publish')); - $is_bbpress_post_type = in_array($post_type, $self->bbPressPostTypes(), true); - - if ($is_bbpress_post_type) { - if (in_array($old_status, array('publish', 'private', 'closed', 'spam', 'hidden'), true)) { - if (in_array($new_status, $unpublished_post_statuses, true)) { - $counter += $self->autoClearPostCache($post_id, true); - } - } - } elseif (in_array($old_status, array('publish', 'private'), true)) { - if (in_array($new_status, $unpublished_post_statuses, true)) { - $counter += $self->autoClearPostCache($post_id, true); - } - } - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpSettingUtils.php b/src/includes/closures/Plugin/WcpSettingUtils.php deleted file mode 100644 index 728b345..0000000 --- a/src/includes/closures/Plugin/WcpSettingUtils.php +++ /dev/null @@ -1,31 +0,0 @@ -autoClearCacheOnSettingChanges = function () use ($self) { - $counter = 0; // Initialize. - $pagenow = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : ''; - $settings_updated = !empty($_REQUEST['settings-updated']); - - if (!is_null($done = &$self->cacheKey('autoClearCacheOnSettingChanges', array($pagenow, $settings_updated)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if ($pagenow === 'options-general.php' && $settings_updated) { - $counter += $self->autoClearCache(); - } elseif ($pagenow === 'options-reading.php' && $settings_updated) { - $counter += $self->autoClearCache(); - } elseif ($pagenow === 'options-discussion.php' && $settings_updated) { - $counter += $self->autoClearCache(); - } elseif ($pagenow === 'options-permalink.php' && $settings_updated) { - $counter += $self->autoClearCache(); - } -}; diff --git a/src/includes/closures/Plugin/WcpSitemapUtils.php b/src/includes/closures/Plugin/WcpSitemapUtils.php deleted file mode 100644 index ad7fcfd..0000000 --- a/src/includes/closures/Plugin/WcpSitemapUtils.php +++ /dev/null @@ -1,47 +0,0 @@ -autoClearXmlSitemapsCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearXmlSitemapsCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_xml_sitemaps_enable']) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_xml_sitemap_patterns']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - if (!($regex_frags = $self->buildHostCachePathRegexFragsFromWcUris($self->options['cache_clear_xml_sitemap_patterns'], ''))) { - return $counter; // There are no patterns to look for. - } - $regex = $self->buildHostCachePathRegex('', '\/'.$regex_frags.'\.'); - $counter += $self->clearFilesFromHostCacheDir($regex); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for XML sitemaps; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpTermUtils.php b/src/includes/closures/Plugin/WcpTermUtils.php deleted file mode 100644 index 8f1adc2..0000000 --- a/src/includes/closures/Plugin/WcpTermUtils.php +++ /dev/null @@ -1,139 +0,0 @@ -autoClearPostTermsCache = function ($post_id, $force = false) use ($self) { - $counter = 0; // Initialize. - $enqueued_notices = 0; // Initialize. - - if (!($post_id = (integer) $post_id)) { - return $counter; // Nothing to do. - } - if (!is_null($done = &$self->cacheKey('autoClearPostTermsCache', array($post_id, $force)))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { - return $counter; // Nothing to do. - } - if (!$self->options['cache_clear_term_category_enable'] && !$self->options['cache_clear_term_post_tag_enable'] && !$self->options['cache_clear_term_other_enable']) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $post_status = get_post_status($post_id); // Cache this. - - if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish']) - && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts') - && strpos(wp_get_referer(), '/post-new.php') !== false - ) { - $post_status = 'publish'; // A new post being published now. - } - if (in_array($post_status, array('inherit', 'auto-draft'), true)) { - return $counter; // Nothing to do. Note: `inherit` = revision. - } - if (in_array($post_status, array('draft', 'pending', 'future'), true) && !$force) { - return $counter; // Nothing to do; i.e., NOT forcing in this case. - } - /* - * Build an array of available taxonomies for this post (as taxonomy objects). - */ - $taxonomies = get_object_taxonomies(get_post($post_id), 'objects'); - - if (!is_array($taxonomies)) { - return $counter; // Nothing to do. - } - /* - * Build an array of terms associated with this post for each taxonomy. - * Also save taxonomy label information for Dashboard messaging later. - */ - $terms = array(); - $taxonomy_labels = array(); - - foreach ($taxonomies as $_taxonomy) { - if (// Check if this is a taxonomy/term that we should clear. - ($_taxonomy->name === 'category' && !$self->options['cache_clear_term_category_enable']) - || ($_taxonomy->name === 'post_tag' && !$self->options['cache_clear_term_post_tag_enable']) - || ($_taxonomy->name !== 'category' && $_taxonomy->name !== 'post_tag' && !$self->options['cache_clear_term_other_enable']) - ) { - continue; // Continue; nothing to do for this taxonomy. - } - if (is_array($_terms = wp_get_post_terms($post_id, $_taxonomy->name))) { - $terms = array_merge($terms, $_terms); - if (empty($_taxonomy->labels->singular_name) || $_taxonomy->labels->singular_name === '') { - $taxonomy_labels[$_taxonomy->name] = $_taxonomy->name; - } else { - $taxonomy_labels[$_taxonomy->name] = $_taxonomy->labels->singular_name; - } - } - } - unset($_taxonomy, $_terms); - - if (empty($terms)) { - return $counter; // Nothing to do. - } - /* - * Build an array of terms with term names, - * permalinks, and associated taxonomy labels. - */ - $terms_to_clear = array(); - $_i = 0; - - foreach ($terms as $_term) { - if (($_link = get_term_link($_term))) { - $terms_to_clear[$_i]['permalink'] = $_link; - $terms_to_clear[$_i]['term_name'] = $_term->name; - if (!empty($taxonomy_labels[$_term->taxonomy])) { - $terms_to_clear[$_i]['taxonomy_label'] = $taxonomy_labels[$_term->taxonomy]; - } else { - $terms_to_clear[$_i]['taxonomy_label'] = $_term->taxonomy; - } - } - ++$_i; // Array index counter. - } - unset($_term, $_link, $_i); - - if (empty($terms_to_clear)) { - return $counter; // Nothing to do. - } - foreach ($terms_to_clear as $_term) { - $_term_regex = $self->buildHostCachePathRegex($_term['permalink']); - $_term_counter = $self->clearFilesFromHostCacheDir($_term_regex); - $counter += $_term_counter; // Add to overall counter. - - if ($_term_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected changes. Found %2$s in the cache for %3$s: %4$s; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($_term_counter)), esc_html($_term['taxonomy_label']), esc_html($_term['term_name']))); - ++$enqueued_notices; // Increment enqueued notices counter. - } - } - unset($_term, $_term_regex, $_term_counter); // Housekeeping. - - $counter += $self->autoClearXmlFeedsCache('post-terms', $post_id); - - return $counter; -}; diff --git a/src/includes/closures/Plugin/WcpUpdaterUtils.php b/src/includes/closures/Plugin/WcpUpdaterUtils.php deleted file mode 100644 index 215ad69..0000000 --- a/src/includes/closures/Plugin/WcpUpdaterUtils.php +++ /dev/null @@ -1,99 +0,0 @@ -autoClearOnUpgraderProcessComplete = function (\WP_Upgrader $upgrader_instance, array $data) use ($self) { - $counter = 0; // Initialize. - - switch (!empty($data['type']) ? $data['type'] : '') { - case 'plugin': // Plugin upgrade. - - $multi_plugin_update = $single_plugin_update = false; - $upgrading_active_plugin = false; // Initialize. - - if (!empty($data['bulk']) && !empty($data['plugins']) && is_array($data['plugins'])) { - $multi_plugin_update = true; - } elseif (!empty($data['plugin']) && is_string($data['plugin'])) { - $single_plugin_update = true; - } - if ($multi_plugin_update) { - foreach ($data['plugins'] as $_plugin) { - if ($_plugin && is_string($_plugin) && is_plugin_active($_plugin)) { - $upgrading_active_plugin = true; - break; // Got what we need here. - } - } - unset($_plugin); // Housekeeping. - } elseif ($single_plugin_update && is_plugin_active($data['plugin'])) { - $upgrading_active_plugin = true; - } - if ($upgrading_active_plugin) { - $counter += $self->autoClearCache(); - } - break; // Break switch. - - case 'theme': // Theme upgrade. - - $current_active_theme = wp_get_theme(); - $current_active_theme_parent = $current_active_theme->parent(); - $multi_theme_update = $single_theme_update = false; - $upgrading_active_parent_theme = $upgrading_active_theme = false; - - if (!empty($data['bulk']) && !empty($data['themes']) && is_array($data['themes'])) { - $multi_theme_update = true; - } elseif (!empty($data['theme']) && is_string($data['theme'])) { - $single_theme_update = true; - } - if ($multi_theme_update) { - foreach ($data['themes'] as $_theme) { - if (!$_theme || !is_string($_theme) || !($_theme_obj = wp_get_theme($_theme))) { - continue; // Unable to acquire theme object instance. - } - if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) { - $upgrading_active_parent_theme = true; - break; // Got what we needed here. - } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) { - $upgrading_active_theme = true; - break; // Got what we needed here. - } - } - unset($_theme, $_theme_obj); // Housekeeping. - } elseif ($single_theme_update && ($_theme_obj = wp_get_theme($data['theme']))) { - if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) { - $upgrading_active_parent_theme = true; - } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) { - $upgrading_active_theme = true; - } - } - unset($_theme_obj); // Housekeeping. - - if ($upgrading_active_theme || $upgrading_active_parent_theme) { - $counter += $self->autoClearCache(); - } - break; // Break switch. - - case 'core': // Core upgrade. - default: // Or any other sort of upgrade. - - $counter += $self->autoClearCache(); - - break; // Break switch. - } -}; diff --git a/src/includes/closures/Plugin/WcpUtils.php b/src/includes/closures/Plugin/WcpUtils.php deleted file mode 100644 index ce34b96..0000000 --- a/src/includes/closures/Plugin/WcpUtils.php +++ /dev/null @@ -1,341 +0,0 @@ -pre_post_update_post_permalink = array(); - -/* - * Wipes out all cache files. - * - * @since 150422 Rewrite. - * - * @param bool $manually TRUE if wiping is done manually. - * - * @throws \Exception If a wipe failure occurs. - * - * @return int Total files wiped by this routine. - */ -$self->wipeCache = function ($manually = false) use ($self) { - $counter = 0; // Initialize. - - if (!$manually && $self->disableAutoWipeCacheRoutines()) { - return $counter; // Nothing to do. - } - @set_time_limit(1800); // @TODO Display a warning. - - if (is_dir($cache_dir = $self->cacheDir())) { - $regex = $self->assembleCachePathRegex('', '.+'); - $counter += $self->wipeFilesFromCacheDir($regex); - } - - - - - - return $counter; -}; -$self->wipe_cache = $self->wipeCache; // Back compat. - -/* - * Clears cache files (current blog). - * - * @since 150422 Rewrite. - * - * @param bool $manually TRUE if clearing is done manually. - * - * @throws \Exception If a clearing failure occurs. - * - * @return int Total files cleared by this routine. - */ -$self->clearCache = function ($manually = false) use ($self) { - $counter = 0; // Initialize. - - if (!$manually && $self->disableAutoClearCacheRoutines()) { - return $counter; // Nothing to do. - } - @set_time_limit(1800); // @TODO Display a warning. - - if (is_dir($cache_dir = $self->cacheDir())) { - $regex = $self->buildHostCachePathRegex('', '.+'); - $counter += $self->clearFilesFromHostCacheDir($regex); - } - - - - - - return $counter; -}; -$self->clear_cache = $self->clearCache; // Back compat. - -/* - * Purges expired cache files (current blog). - * - * @since 150422 Rewrite. - * - * @param bool $manually TRUE if purging is done manually. - * - * @throws \Exception If a purge failure occurs. - * - * @return int Total files purged by this routine. - */ -$self->purgeCache = function ($manually = false) use ($self) { - $counter = 0; // Initialize. - - if (!$manually && $self->disableAutoPurgeCacheRoutines()) { - return $counter; // Nothing to do. - } - @set_time_limit(1800); // @TODO Display a warning. - - if (is_dir($cache_dir = $self->cacheDir())) { - $regex = $self->buildHostCachePathRegex('', '.+'); - $counter += $self->purgeFilesFromHostCacheDir($regex); - } - - return $counter; -}; -$self->purge_cache = $self->purgeCache; // Back compat. - -/* - * Wurges (purges) all expired cache files; like wipe, but expired files only. - * - * @since 151002 Look at entire cache directory. - * - * @param bool $manually TRUE if wurging is done manually. - * - * @throws \Exception If a wurge failure occurs. - * - * @return int Total files wurged by this routine. - */ -$self->wurgeCache = function ($manually = false) use ($self) { - $counter = 0; // Initialize. - - if (!$manually && $self->disableAutoPurgeCacheRoutines()) { - return $counter; // Nothing to do. - } - @set_time_limit(1800); // @TODO Display a warning. - - if (is_dir($cache_dir = $self->cacheDir())) { - $regex = $self->assembleCachePathRegex('', '.+'); - $counter += $self->wurgeFilesFromCacheDir($regex); - } - - return $counter; -}; - -/* - * Automatically wipes out all cache files. - * - * @attaches-to Nothing at this time. - * - * @since 150422 Rewrite. - * - * @return int Total files wiped by this routine (if any). - * - * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. - * This is called upon whenever options are saved and/or restored though. - */ -$self->autoWipeCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoWipeCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if ($self->disableAutoWipeCacheRoutines()) { - return $counter; // Nothing to do. - } - $counter += $self->wipeCache(); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected significant changes. Found %2$s in the cache; auto-wiping.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - return $counter; -}; - -/* - * Automatically clears all cache files (current host). - * - * @attaches-to `switch_theme` hook. - * - * @attaches-to `wp_create_nav_menu` hook. - * @attaches-to `wp_update_nav_menu` hook. - * @attaches-to `wp_delete_nav_menu` hook. - * - * @attaches-to `create_term` hook. - * @attaches-to `edit_terms` hook. - * @attaches-to `delete_term` hook. - * - * @attaches-to `add_link` hook. - * @attaches-to `edit_link` hook. - * @attaches-to `delete_link` hook. - * - * @since 150422 Rewrite. - * - * @return int Total files cleared by this routine (if any). - * - * @note This is also called upon during plugin activation. - */ -$self->autoClearCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if ($self->disableAutoClearCacheRoutines()) { - return $counter; // Nothing to do. - } - $counter += $self->clearCache(); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected important site changes. Found %2$s in the cache for this site; auto-clearing.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - return $counter; -}; - -/* - * Automatically purges all cache files (current host). - * - * @attaches-to Nothing at this time. - * - * @since 151002 While working on directory stats. - * - * @return int Total files purged by this routine. - * - * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. - */ -$self->autoPurgeCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoPurgeCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if ($self->disableAutoPurgeCacheRoutines()) { - return $counter; // Nothing to do. - } - $counter += $self->purgeCache(); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected important site changes. Found %2$s in the cache for this site that were expired; auto-purging.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - return $counter; -}; - -/* - * Automatically wurges all cache files. - * - * @attaches-to Nothing at this time. - * - * @since 151002 While working on directory stats. - * - * @return int Total files wurged by this routine. - * - * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. - */ -$self->autoWurgeCache = function () use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoWurgeCache'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if (!$self->options['enable']) { - return $counter; // Nothing to do. - } - if ($self->disableAutoPurgeCacheRoutines()) { - return $counter; // Nothing to do. - } - $counter += $self->wurgeCache(); - - if ($counter && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueNotice(''. - sprintf(__('%1$s: detected important site changes. Found %2$s in the cache that were expired; auto-purging.', 'comet-cache'), esc_html(NAME), esc_html($self->i18nFiles($counter)))); - } - return $counter; -}; - -/* - * Allows a site owner to disable the automatic cache wiping routines. - * - * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_wipe_cache_routines` to return TRUE, - * in which case this method returns TRUE, otherwise it returns FALSE. - * - * @since 150422 Rewrite. - * - * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. - */ -$self->disableAutoWipeCacheRoutines = function () use ($self) { - $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_wipe_cache_routines', false); - - if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueMainNotice(''. - sprintf(__('%1$s: detected significant changes that would normally trigger cache wiping routines. However, cache wiping routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME))); - } - return $is_disabled; -}; - -/* - * Allows a site owner to disable the automatic cache clearing routines. - * - * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_clear_cache_routines` to return TRUE, - * in which case this method returns TRUE, otherwise it returns FALSE. - * - * @since 150422 Rewrite. - * - * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. - */ -$self->disableAutoClearCacheRoutines = function () use ($self) { - $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_clear_cache_routines', false); - - if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueMainNotice(''. - sprintf(__('%1$s: detected important site changes that would normally trigger cache clearing routines. However, cache clearing routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME))); - } - return $is_disabled; -}; - -/* - * Allows a site owner to disable the automatic cache purging routines. - * - * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_purge_cache_routines` to return TRUE, - * in which case this method returns TRUE, otherwise it returns FALSE. - * - * @since 151002 While working on directory stats. - * - * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. - */ -$self->disableAutoPurgeCacheRoutines = function () use ($self) { - $is_disabled = (boolean) $self->applyWpFilters(GLOBAL_NS.'_disable_auto_purge_cache_routines', false); - - if ($is_disabled && is_admin() && (!IS_PRO || $self->options['change_notifications_enable'])) { - $self->enqueueMainNotice(''. - sprintf(__('%1$s: detected important site changes that would normally trigger cache purging routines. However, cache purging routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME))); - } - return $is_disabled; -}; diff --git a/src/includes/closures/Plugin/WcpWooCommerceUtils.php b/src/includes/closures/Plugin/WcpWooCommerceUtils.php deleted file mode 100644 index b02c2c5..0000000 --- a/src/includes/closures/Plugin/WcpWooCommerceUtils.php +++ /dev/null @@ -1,24 +0,0 @@ -autoClearPostCacheOnWooCommerceSetStock = function ($product) use ($self) { - $counter = 0; // Initialize. - - if (!is_null($done = &$self->cacheKey('autoClearPostCacheOnWooCommerceSetStock'))) { - return $counter; // Already did this. - } - $done = true; // Flag as having been done. - - if(class_exists('\\WooCommerce')) { - $counter += $self->autoClearPostCache($product->id); - } -}; diff --git a/src/includes/closures/Shared/BlogUtils.php b/src/includes/closures/Shared/BlogUtils.php deleted file mode 100644 index aefa257..0000000 --- a/src/includes/closures/Shared/BlogUtils.php +++ /dev/null @@ -1,31 +0,0 @@ -blogDetails = function ($blog_id = 0) use ($self) { - if (!is_multisite() || $self->isAdvancedCache()) { - return null; // Not possible. - } - if (($blog_id = (integer) $blog_id) < 0) { - $blog_id = (integer) get_current_site()->blog_id; - } - if (!$blog_id) { - $blog_id = (integer) get_current_blog_id(); - } - if (!$blog_id || $blog_id < 0) { - return null; // Not possible. - } - $details = get_blog_details($blog_id); - - return is_object($details) ? $details : null; -}; diff --git a/src/includes/closures/Shared/CacheDirUtils.php b/src/includes/closures/Shared/CacheDirUtils.php deleted file mode 100644 index 25a6089..0000000 --- a/src/includes/closures/Shared/CacheDirUtils.php +++ /dev/null @@ -1,643 +0,0 @@ -cacheDir = function ($rel_path = '') use ($self) { - $rel_path = (string) $rel_path; - - if ($self->isAdvancedCache()) { - $cache_dir = defined('COMET_CACHE_DIR') ? COMET_CACHE_DIR : ''; - } elseif (!empty($self->cache_sub_dir)) { - $cache_dir = $self->wpContentBaseDirTo($self->cache_sub_dir); - } - if (empty($cache_dir)) { - throw new \Exception(__('Unable to determine cache directory location.', 'comet-cache')); - } - return rtrim($cache_dir, '/').($rel_path ? '/'.ltrim($rel_path) : ''); -}; - -/* - * Wipe files from the cache directory (for all hosts/blogs); - * i.e., those that match a specific regex pattern. - * - * @since 151002 While working on directory stats. - * - * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}. - * - * @return integer Total files wiped by this routine. - */ -$self->wipeFilesFromCacheDir = function ($regex) use ($self) { - return $self->deleteFilesFromCacheDir($regex); -}; - -/* - * Clear files from the cache directory (for the current host); - * i.e., those that match a specific regex pattern. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}. - * - * @return integer Total files cleared by this routine (if any). - */ -$self->clearFilesFromHostCacheDir = function ($regex) use ($self) { - return $self->deleteFilesFromHostCacheDir($regex); -}; - -/* - * Wurge (purge) files from the cache directory (for all hosts/blogs); - * i.e., those that match a specific regex pattern. - * - * @since 151002 While working on directory stats. - * - * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}. - * - * @return integer Total files wurged by this routine. - */ -$self->wurgeFilesFromCacheDir = function ($regex) use ($self) { - return $self->deleteFilesFromCacheDir($regex, true); -}; - -/* - * Purge files from the cache directory (for the current host); - * i.e., those that match a specific regex pattern. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}. - * - * @return integer Total files purged by this routine (if any). - */ -$self->purgeFilesFromHostCacheDir = function ($regex) use ($self) { - return $self->deleteFilesFromHostCacheDir($regex, true); -}; - -/* - * Delete files from the cache directory (for all hosts/blogs); - * i.e., those that match a specific regex pattern. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $regex A `/[regex pattern]/`; relative to the cache directory. - * e.g. `/^http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` - * - * Or, this can also be a full/absolute regex pattern against an absolute path; - * provided that it always starts with `/^`; including the full absolute cache/host directory path. - * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` - * - * @param boolean $check_max_age Check max age? i.e., use purge behavior? - * - * @return integer Total files deleted by this routine (if any). - * - * @throws \Exception If unable to delete a file for any reason. - * - * @TODO Optimize this for multisite networks w/ a LOT of child blogs. - * @TODO Optimize this for extremely large sites. A LOT of files here could slow things down. - * This class member is currently used in wiping and purging for a network. So there is the potential for a LOT of files in a single scan. - * See also: - */ -$self->deleteFilesFromCacheDir = function ($regex, $check_max_age = false) use ($self) { - $counter = 0; // Initialize. - - if (!($regex = (string) $regex)) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $cache_dir = $self->nDirSeps($cache_dir); - - if ($check_max_age && $self->isAdvancedCache()) { - throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache')); - } - if ($check_max_age && !($max_age = strtotime('-'.$self->options['cache_max_age']))) { - return $counter; // Invalid cache expiration time. - } - /* ------- Begin lock state... ----------- */ - - $cache_lock = $self->cacheLock(); // Lock cache writes. - - clearstatcache(); // Clear stat cache to be sure we have a fresh start below. - - $cache_dir_tmp = $self->addTmpSuffix($cache_dir); // Temporary directory. - - $cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory. - $cache_dir_tmp_regex = '\\/'.ltrim($cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`. - $cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($cache_dir.'/', '/'), '', $cache_dir_tmp_regex); - - $cache_dir_tmp_regex = ltrim($cache_dir_tmp_regex, '^\\/'); - if (strpos($cache_dir_tmp_regex, '(?:\/') === 0 || strpos($cache_dir_tmp_regex, '(\/') === 0) { - $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp, '/').$cache_dir_tmp_regex; - } else { - $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp.'/', '/').$cache_dir_tmp_regex; - } - # if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND); - // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it. - - if (!rename($cache_dir, $cache_dir_tmp)) { - throw new \Exception(sprintf(__('Unable to delete files. Rename failure on directory: `%1$s`.', 'comet-cache'), $cache_dir)); - } - foreach (($_dir_regex_iteration = $self->dirRegexIteration($cache_dir_tmp, $cache_dir_tmp_regex)) as $_resource) { - $_resource_type = $_resource->getType(); - $_sub_path_name = $_resource->getSubpathname(); - $_path_name = $_resource->getPathname(); - - if ($_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) { - continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc. - // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes. - } - switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. - - case 'link': // Symbolic links; i.e., 404 errors. - - if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) { - if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) { - if ($_lstat['mtime'] >= $max_age) { - break; // Break switch. - } - } - } - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each link we delete. - - break; // Break switch handler. - - case 'file': // Regular files; i.e., not symlinks. - - if ($check_max_age && !empty($max_age)) { - if ($_resource->getMTime() >= $max_age) { - break; // Break switch. - } - } - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each file we delete. - - break; // Break switch handler. - - case 'dir': // A regular directory; i.e., not a symlink. - - if ($regex !== '/^.+/i') { - break; // Not deleting everything. - } - if ($check_max_age && !empty($max_age)) { - break; // Not deleting everything. - } - if (!rmdir($_path_name)) { - $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); - } - # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here. - - break; // Break switch handler. - - default: // Something else that is totally unexpected here. - $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); - } - } - unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping. - - if (!rename($cache_dir_tmp, $cache_dir)) { - $self->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $cache_dir_tmp)); - } - /* ------- End lock state... ------------- */ - - $self->cacheUnlock($cache_lock); // Release. - - return $counter; -}; - -/* - * Delete files from the cache directory (for the current host); - * i.e., those that match a specific regex pattern. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $regex A `/[regex pattern]/`; relative to the host cache directory. - * e.g. `/^my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` - * - * Or, this can also be a full/absolute regex pattern against an absolute path; - * provided that it always starts with `/^`; including the full absolute cache/host directory path. - * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` - * - * @param boolean $check_max_age Check max age? i.e., use purge behavior? - * - * @param boolean $___considering_domain_mapping For internal use only. - * @param boolean $___consider_domain_mapping_host_token For internal use only. - * @param boolean $___consider_domain_mapping_host_base_dir_tokens For internal use only. - * - * @return integer Total files deleted by this routine (if any). - * - * @throws \Exception If unable to delete a file for any reason. - */ -$self->deleteFilesFromHostCacheDir = function ( - $regex, - $check_max_age = false, - $___considering_domain_mapping = false, - $___consider_domain_mapping_host_token = null, - $___consider_domain_mapping_host_base_dir_tokens = null -) use ($self) { - $counter = 0; // Initialize. - - if (!($regex = (string) $regex)) { - return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $self->cacheDir())) { - return $counter; // Nothing to do. - } - $cache_dir = $self->nDirSeps($cache_dir); // Normalize. - $host_token = $current_host_token = $self->hostToken(); - $host_base_dir_tokens = $current_host_base_dir_tokens = $self->hostBaseDirTokens(); - - if ($___considering_domain_mapping && isset($___consider_domain_mapping_host_token, $___consider_domain_mapping_host_base_dir_tokens)) { - $host_token = (string) $___consider_domain_mapping_host_token; - $host_base_dir_tokens = (string) $___consider_domain_mapping_host_base_dir_tokens; - } - if (!$host_token) { // Must have a host in the sub-routine below. - throw new \Exception(__('Invalid argument; host token empty!', 'comet-cache')); - } - if ($check_max_age && $self->isAdvancedCache()) { - throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache')); - } - if ($check_max_age && !($max_age = strtotime('-'.$self->options['cache_max_age']))) { - return $counter; // Invalid cache expiration time. - } - /* ------- Begin lock state... ----------- */ - - $cache_lock = $self->cacheLock(); // Lock cache writes. - - clearstatcache(); // Clear stat cache to be sure we have a fresh start below. - - foreach (array('http', 'https') as $_host_scheme) { - $_host_url = $_host_scheme.'://'.$host_token.$host_base_dir_tokens; - $_host_cache_path_flags = CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT; - $_host_cache_path = $self->buildCachePath($_host_url, '', '', $_host_cache_path_flags); - $_host_cache_dir = $self->nDirSeps($cache_dir.'/'.$_host_cache_path); // Normalize. - - if (!$_host_cache_dir || !is_dir($_host_cache_dir)) { - // On a multisite install this may have a cache sub-directory. - // e.g., `http/example-com[[-base]-child1][[/base]/child1]` instead of `http/example-com`. - continue; // Nothing to do. - } - $_host_cache_dir_tmp = $self->addTmpSuffix($_host_cache_dir); // Temporary directory. - - $_host_cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory. - $_host_cache_dir_tmp_regex = '\\/'.ltrim($_host_cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`. - $_host_cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($_host_cache_path.'/', '/'), '', $_host_cache_dir_tmp_regex); - $_host_cache_dir_tmp_regex = $self->strIreplaceOnce(preg_quote($_host_cache_dir.'/', '/'), '', $_host_cache_dir_tmp_regex); - - $_host_cache_dir_tmp_regex = ltrim($_host_cache_dir_tmp_regex, '^\\/'); - if (strpos($_host_cache_dir_tmp_regex, '(?:\/') === 0 || strpos($_host_cache_dir_tmp_regex, '(\/') === 0) { - $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp, '/').$_host_cache_dir_tmp_regex; - } else { - $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp.'/', '/').$_host_cache_dir_tmp_regex; - } - #if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($_host_cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND); - // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it. - - if (!rename($_host_cache_dir, $_host_cache_dir_tmp)) { - throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir)); - } - foreach (($_dir_regex_iteration = $self->dirRegexIteration($_host_cache_dir_tmp, $_host_cache_dir_tmp_regex)) as $_resource) { - $_resource_type = $_resource->getType(); - $_sub_path_name = $_resource->getSubpathname(); - $_path_name = $_resource->getPathname(); - - if ($_host_cache_dir === $cache_dir && $_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) { - continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc. - // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes. - } - switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. - - case 'link': // Symbolic links; i.e., 404 errors. - - if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) { - if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) { - if ($_lstat['mtime'] >= $max_age) { - break; // Break switch. - } - } - } - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each link we delete. - - break; // Break switch handler. - - case 'file': // Regular files; i.e., not symlinks. - - if ($check_max_age && !empty($max_age)) { - if ($_resource->getMTime() >= $max_age) { - break; // Break switch handler. - } - } - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each file we delete. - - break; // Break switch handler. - - case 'dir': // A regular directory; i.e., not a symlink. - - if ($regex !== '/^.+/i') { - break; // Not deleting everything. - } - if ($check_max_age && !empty($max_age)) { - break; // Not deleting everything. - } - if (!rmdir($_path_name)) { - $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); - } - # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here. - - break; // Break switch handler. - - default: // Something else that is totally unexpected here. - $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); - } - } - unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping. - - if (!rename($_host_cache_dir_tmp, $_host_cache_dir)) { - $self->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir_tmp)); - } - } - unset($_host_scheme, $_host_url, $_host_cache_path_flags, $_host_cache_path, $_host_cache_dir, $_host_cache_dir_tmp, $_host_cache_dir_tmp_regex); - - /* ------- End lock state... ------------- */ - - $self->cacheUnlock($cache_lock); // Release. - - /* ------- Include domain mapping variations also. ------- */ - - if (!$___considering_domain_mapping && is_multisite() && $self->canConsiderDomainMapping()) { - $domain_mapping_variations = array(); // Initialize array of domain variations. - - if (($_host_token_for_blog = $self->hostTokenForBlog())) { - $_host_base_dir_tokens_for_blog = $self->hostBaseDirTokensForBlog(); - $domain_mapping_variations[] = array('host_token' => $_host_token_for_blog, 'host_base_dir_tokens' => $_host_base_dir_tokens_for_blog); - } // The original blog host; i.e., without domain mapping. - unset($_host_token_for_blog, $_host_base_dir_tokens_for_blog); // Housekeeping. - - foreach ($self->domainMappingBlogDomains() as $_domain_mapping_blog_domain) { - if (($_domain_host_token_for_blog = $self->hostTokenForBlog(false, true, $_domain_mapping_blog_domain))) { - $_domain_host_base_dir_tokens_for_blog = $self->hostBaseDirTokensForBlog(false, true); // This is only a formality. - $domain_mapping_variations[] = array('host_token' => $_domain_host_token_for_blog, 'host_base_dir_tokens' => $_domain_host_base_dir_tokens_for_blog); - } - } // This includes all of the domain mappings configured for the current blog ID. - unset($_domain_mapping_blog_domain, $_domain_host_token_for_blog, $_domain_host_base_dir_tokens_for_blog); // Housekeeping. - - foreach ($domain_mapping_variations as $_domain_mapping_variation) { - if ($_domain_mapping_variation['host_token'] === $current_host_token && $_domain_mapping_variation['host_base_dir_tokens'] === $current_host_base_dir_tokens) { - continue; // Exclude current tokens. They were already iterated above. - } - $counter += $self->deleteFilesFromHostCacheDir($regex, $check_max_age, true, $_domain_mapping_variation['host_token'], $_domain_mapping_variation['host_base_dir_tokens']); - } - unset($_domain_mapping_variation); // Housekeeping. - } - return $counter; -}; - -/* - * Delete all files/dirs from a directory (for all schemes/hosts); - * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $dir The directory from which to delete files/dirs. - * - * SECURITY: This directory MUST be located inside the `/wp-content/` directory. - * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. - * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. - * - * @param boolean $delete_dir_too Delete parent? i.e., delete the `$dir` itself also? - * - * @return integer Total files/directories deleted by this routine (if any). - * - * @throws \Exception If unable to delete a file/directory for any reason. - */ -$self->deleteAllFilesDirsIn = function ($dir, $delete_dir_too = false) use ($self) { - $counter = 0; // Initialize. - - if (!($dir = trim((string) $dir)) || !is_dir($dir)) { - return $counter; // Nothing to do. - } - $dir = $self->nDirSeps($dir); - $dir_temp = $self->addTmpSuffix($dir); - $wp_content_dir = $self->nDirSeps(WP_CONTENT_DIR); - $wp_content_dir_regex = preg_quote($wp_content_dir, '/'); - - if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) { - return $counter; // Security flag; do nothing in this case. - } - if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) { - return $counter; // Security flag; do nothing in this case. - } - /* ------- Begin lock state... ----------- */ - - $cache_lock = $self->cacheLock(); // Lock cache writes. - - clearstatcache(); // Clear stat cache to be sure we have a fresh start below. - - if (!rename($dir, $dir_temp)) { - throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir)); - } - foreach (($_dir_regex_iteration = $self->dirRegexIteration($dir_temp, '/.+/')) as $_resource) { - $_resource_type = $_resource->getType(); - $_sub_path_name = $_resource->getSubpathname(); - $_path_name = $_resource->getPathname(); - - switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. - - case 'link': // Symbolic links; i.e., 404 errors. - - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each link we delete. - - break; // Break switch handler. - - case 'file': // Regular files; i.e., not symlinks. - - if (!unlink($_path_name)) { - $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each file we delete. - - break; // Break switch handler. - - case 'dir': // A regular directory; i.e., not a symlink. - - if (!rmdir($_path_name)) { - $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); - } - # ++$counter; // Increment counter for each directory we delete. ~ NO don't do that here. - - break; // Break switch handler. - - default: // Something else that is totally unexpected here. - $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); - } - } - unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping. - - if (!rename($dir_temp, $dir)) { - $self->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. - throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir_temp)); - } - if ($delete_dir_too) { - if (!rmdir($dir)) { - throw new \Exception(sprintf(__('Unable to delete directory: `%1$s`.', 'comet-cache'), $dir)); - } - ++$counter; // Increment counter for each directory we delete. - } - /* ------- End lock state... ------------- */ - - $self->cacheUnlock($cache_lock); // Release. - - return $counter; -}; - -/* - * Erase all files/dirs from a directory (for all schemes/hosts); - * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. - * - * WARNING: This does NO LOCKING and NO ATOMIC deletions. - * - * @since 150821 Improving recovery under stress. - * - * @param string $dir The directory from which to erase files/dirs. - * - * SECURITY: This directory MUST be located inside the `/wp-content/` directory. - * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. - * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. - * - * @param boolean $erase_dir_too Erase parent? i.e., erase the `$dir` itself also? - * - * @return integer Total files/directories erased by this routine (if any). - * - * @throws \Exception If unable to erase a file/directory for any reason. - */ -$self->eraseAllFilesDirsIn = function ($dir, $erase_dir_too = false) use ($self) { - $counter = 0; // Initialize. - - if (!($dir = trim((string) $dir)) || !is_dir($dir)) { - return $counter; // Nothing to do. - } - $dir = $self->nDirSeps($dir); - $wp_content_dir = $self->nDirSeps(WP_CONTENT_DIR); - $wp_content_dir_regex = preg_quote($wp_content_dir, '/'); - - if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) { - return $counter; // Security flag; do nothing in this case. - } - if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) { - return $counter; // Security flag; do nothing in this case. - } - clearstatcache(); // Clear stat cache to be sure we have a fresh start below. - - foreach (($_dir_regex_iteration = $self->dirRegexIteration($dir, '/.+/')) as $_resource) { - $_resource_type = $_resource->getType(); - $_sub_path_name = $_resource->getSubpathname(); - $_path_name = $_resource->getPathname(); - - switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. - - case 'link': // Symbolic links; i.e., 404 errors. - - if (!unlink($_path_name)) { - throw new \Exception(sprintf(__('Unable to erase symlink: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each link we erase. - - break; // Break switch handler. - - case 'file': // Regular files; i.e., not symlinks. - - if (!unlink($_path_name)) { - throw new \Exception(sprintf(__('Unable to erase file: `%1$s`.', 'comet-cache'), $_path_name)); - } - ++$counter; // Increment counter for each file we erase. - - break; // Break switch handler. - - case 'dir': // A regular directory; i.e., not a symlink. - - if (!rmdir($_path_name)) { - throw new \Exception(sprintf(__('Unable to erase dir: `%1$s`.', 'comet-cache'), $_path_name)); - } - # ++$counter; // Increment counter for each directory we erase. ~ NO don't do that here. - - break; // Break switch handler. - - default: // Something else that is totally unexpected here. - throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); - } - } - unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping. - - if ($erase_dir_too) { - if (!rmdir($dir)) { - throw new \Exception(sprintf(__('Unable to erase directory: `%1$s`.', 'comet-cache'), $dir)); - } - ++$counter; // Increment counter for each directory we erase. - } - return $counter; -}; - -/* - * Try to erase all files/dirs from a directory (for all schemes/hosts); - * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. - * - * WARNING: This does NO LOCKING and NO ATOMIC deletions. - * - * @since 150821 Improving recovery under stress. - * - * @param string $dir The directory from which to erase files/dirs. - * - * SECURITY: This directory MUST be located inside the `/wp-content/` directory. - * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. - * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. - * - * @param boolean $erase_dir_too Erase parent? i.e., erase the `$dir` itself also? - * - * @return integer Total files/directories erased by this routine (if any). - */ -$self->tryErasingAllFilesDirsIn = function ($dir, $erase_dir_too = false) use ($self) { - $counter = 0; // Initialize counter. - try { - $counter += $self->eraseAllFilesDirsIn($dir, $erase_dir_too); - } catch (\Exception $exception) { - // Fail softly. - } - return $counter; -}; diff --git a/src/includes/closures/Shared/CacheLockUtils.php b/src/includes/closures/Shared/CacheLockUtils.php deleted file mode 100644 index 627b769..0000000 --- a/src/includes/closures/Shared/CacheLockUtils.php +++ /dev/null @@ -1,80 +0,0 @@ -cacheLock = function () use ($self) { - if ($self->applyWpFilters(GLOBAL_NS.'\\share::disable_cache_locking', false) - || $self->applyWpFilters(GLOBAL_NS.'_disable_cache_locking', false)) { - return false; // Disabled cache locking. - } - if (!($wp_config_file = $self->findWpConfigFile())) { - throw new \Exception(__('Unable to find the wp-config.php file.', 'comet-cache')); - } - $lock_type = 'flock'; // Default lock type. - $lock_type = $self->applyWpFilters(GLOBAL_NS.'\\share::cache_lock_lock_type', $lock_type); - $lock_type = $self->applyWpFilters(GLOBAL_NS.'_cache_lock_type', $lock_type); - - if (!in_array($lock_type, array('flock', 'sem'), true)) { - $lock_type = 'flock'; // Default lock type. - } - if ($lock_type === 'sem' && $self->functionIsPossible('sem_get')) { - if (($ipc_key = ftok($wp_config_file, 'w'))) { - if (($resource = sem_get($ipc_key, 1)) && sem_acquire($resource)) { - return array('type' => 'sem', 'resource' => $resource); - } - } - } - if (!($tmp_dir = $self->getTmpDir())) { - throw new \Exception(__('No writable tmp directory.', 'comet-cache')); - } - $inode_key = fileinode($wp_config_file); - $mutex = $tmp_dir.'/'.SLUG_TD.'-'.$inode_key.'.lock'; - - if (!($resource = fopen($mutex, 'wb')) || !flock($resource, LOCK_EX)) { - throw new \Exception(__('Unable to obtain an exclusive lock.', 'comet-cache')); - } - - @chmod($mutex, 0666); // See https://git.io/v2WAt - - return array('type' => 'flock', 'resource' => $resource); -}; - -/* - * Release an exclusive lock on the cache directory. - * - * @since 150422 Rewrite. Updated 151002 to remove the `array` typecast. - * - * @param array|mixed $lock Type & resource. - */ -$self->cacheUnlock = function ($lock) use ($self) { - if (!is_array($lock)) { - return; // Not possible. - // Or, they disabled cache locking. - } - if (empty($lock['type']) || empty($lock['resource'])) { - return; // Not possible. - } - if (!is_resource($lock['resource'])) { - return; // Not possible. - } - if ($lock['type'] === 'sem') { - sem_release($lock['resource']); - } elseif ($lock['type'] === 'flock') { - flock($lock['resource'], LOCK_UN); - fclose($lock['resource']); - } -}; diff --git a/src/includes/closures/Shared/CachePathConsts.php b/src/includes/closures/Shared/CachePathConsts.php deleted file mode 100644 index 4d7e2ee..0000000 --- a/src/includes/closures/Shared/CachePathConsts.php +++ /dev/null @@ -1,140 +0,0 @@ -cachePathRegexSuffixFrag = function ($regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - if ($regex_suffix_frag === CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) { - return $self->cachePathRegexDefaultSuffixFrag(); - } - return (string) $regex_suffix_frag; -}; - -/* -* Default cache-path suffix frag (regex). -* -* @since 151220 Enhancing translation support. -* -* @return string Default cache-path suffix frag (regex). -* -* @TODO Use conditional to detect the AMP plugin (e.g., `isAmpInstalled()`) to avoid edge cases with the `|\/amp` regex here -*/ -$self->cachePathRegexDefaultSuffixFrag = function () use ($self) { - if ($self->isPlugin() && !empty($GLOBALS['wp_rewrite'])){ - $pagination_base = $GLOBALS['wp_rewrite']->pagination_base; - $comments_pagination_base = $GLOBALS['wp_rewrite']->comments_pagination_base; - return '(?:\/index|\/amp)?(?:\.|\/(?:'.preg_quote($pagination_base, '/').'\/[0-9]+|'.preg_quote($comments_pagination_base, '/').'\-[0-9]+)[.\/])'; - } else { - return '(?:\/index|\/amp)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])'; - } -}; - -/* - * Converts a URL into a `cache/path` based on input `$flags`. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $url The input URL to convert. - * @param string $with_user_token Optional user token (if applicable). - * @param string $with_version_salt Optional version salt (if applicable). - * @param int $flags Optional flags. A bitmask via `CACHE_PATH_*` constants. - * - * @return string The resulting `cache/path` based on the input `$url` & `$flags`. - */ -$self->buildCachePath = function ($url, $with_user_token = '', $with_version_salt = '', $flags = CACHE_PATH_DEFAULT) use ($self) { - # Force parameter types. - - $url = trim((string) $url); - $with_user_token = trim((string) $with_user_token); - $with_version_salt = trim((string) $with_version_salt); - - # Initialize variables. - - $is_multisite = is_multisite(); - $can_consider_domain_mapping = $is_multisite && $self->canConsiderDomainMapping(); - $cache_path = ''; // Initialize cache path being built here. - - # Deal w/ domain mapping considerations. - - if ($flags & CACHE_PATH_CONSIDER_DOMAIN_MAPPING && $is_multisite && $can_consider_domain_mapping) { - if ($flags & CACHE_PATH_REVERSE_DOMAIN_MAPPING) { - $url = $self->domainMappingReverseUrlFilter($url); - } else { - $url = $self->domainMappingUrlFilter($url); - } - } - # Validate the URL we have now. - - if (!$url || !($url_parts = $self->parseUrl($url))) { - return ($cache_path = ''); // Not possible. - } - if (empty($url_parts['scheme']) || $url_parts['scheme'] === '//') { - return ($cache_path = ''); // Not possible. - } - if (empty($url_parts['host'])) { - return ($cache_path = ''); // Not possible. - } - # Initialize additional variables; based on the parsed URL. - - $is_url_domain_mapped = $is_multisite && $can_consider_domain_mapping && $self->domainMappingBlogId($url); - $host_base_dir_tokens = $self->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/'); - - $is_a_multisite_base_dir = $is_multisite && $host_base_dir_tokens && $host_base_dir_tokens !== '/' // Check? - && stripos(!empty($url_parts['path']) ? rtrim($url_parts['path'], '/').'/' : '/', $host_base_dir_tokens) === 0; - - $is_a_multisite_base_dir_root = $is_multisite && $is_a_multisite_base_dir // Save time by using the previous check here. - && strcasecmp(trim($host_base_dir_tokens, '/'), trim(!empty($url_parts['path']) ? $url_parts['path'] : '/', '/')) === 0; - - # Build and return the cache path. - - if (!($flags & CACHE_PATH_NO_SCHEME)) { - $cache_path .= $url_parts['scheme'].'/'; - } - if (!($flags & CACHE_PATH_NO_HOST)) { - $cache_path .= $url_parts['host'].'/'; - - // Put multisite sub-roots into a host directory of their own. - // e.g., `example-com[[-base]-child1]` instead of `example-com`. - if ($is_a_multisite_base_dir && $host_base_dir_tokens && $host_base_dir_tokens !== '/') { - $host_base_dir_suffix = preg_replace('/[^a-z0-9.]/i', '-', rtrim($host_base_dir_tokens, '/')); - $cache_path = rtrim($cache_path, '/').$host_base_dir_suffix.'/'; - } - } - if (!($flags & CACHE_PATH_NO_PATH)) { - if (isset($url_parts['path'][201])) { - $_path_tmp = '/'; // Initialize tmp path. - foreach (explode('/', $url_parts['path']) as $_path_component) { - if (!isset($_path_component[0])) { - continue; // Empty. - } - if (isset($_path_component[201])) { - $_path_component = 'lpc-'.sha1($_path_component); - } - $_path_tmp .= $_path_component.'/'; - } - $url_parts['path'] = $_path_tmp; // Shorter components. - unset($_path_component, $_path_tmp); // Housekeeping. - - if (isset($url_parts['path'][2001])) { - $url_parts['path'] = '/lp-'.sha1($url_parts['path']).'/'; - } - } // Now add the path and check for a possible root `index/` suffix. - if (!empty($url_parts['path']) && strlen($url_parts['path'] = trim($url_parts['path'], '\\/'." \t\n\r\0\x0B"))) { - $cache_path .= $url_parts['path'].'/'; // Add the path as it exists. - - if (!($flags & CACHE_PATH_NO_PATH_INDEX) && $is_multisite && $is_a_multisite_base_dir_root) { - // We should build an `index/` when this ends with a multisite [[/base]/child1] root. - // e.g., `http/example-com[[-base]-child1][[/base]/child1]` is a root directory. - $cache_path .= 'index/'; // Use an index suffix. - } - } elseif (!($flags & CACHE_PATH_NO_PATH_INDEX)) { - $cache_path .= 'index/'; - } - } - if ($self->isExtensionLoaded('mbstring') && mb_check_encoding($cache_path, 'UTF-8')) { - $cache_path = mb_strtolower($cache_path, 'UTF-8'); - } - $cache_path = str_replace('.', '-', strtolower($cache_path)); - - if (!($flags & CACHE_PATH_NO_QUV)) { - if (!($flags & CACHE_PATH_NO_QUERY)) { - if (isset($url_parts['query']) && $url_parts['query'] !== '') { - $cache_path = rtrim($cache_path, '/').'.q/'.md5($url_parts['query']).'/'; - } - } - if (!($flags & CACHE_PATH_NO_USER)) { - if ($with_user_token !== '') { - $cache_path = rtrim($cache_path, '/').'.u/'.str_replace(array('/', '\\'), '-', $with_user_token).'/'; - } - } - if (!($flags & CACHE_PATH_NO_VSALT)) { - if ($with_version_salt !== '') { - $cache_path = rtrim($cache_path, '/').'.v/'.str_replace(array('/', '\\'), '-', $with_version_salt).'/'; - } - } - } - $cache_path = trim(preg_replace(array('/\/+/', '/\.+/'), array('/', '.'), $cache_path), '/'); - - if ($flags & CACHE_PATH_ALLOW_WD_REGEX) { - $cache_path = preg_replace('/[^a-z0-9\/.*\^$]/i', '-', $cache_path); - } elseif ($flags & CACHE_PATH_ALLOW_WILDCARDS) { - $cache_path = preg_replace('/[^a-z0-9\/.*]/i', '-', $cache_path); - } else { - $cache_path = preg_replace('/[^a-z0-9\/.]/i', '-', $cache_path); - } - if (!($flags & CACHE_PATH_NO_EXT)) { - $cache_path .= '.html'; - } - return $cache_path; -}; - -/* - * Regex pattern for a call to `deleteFilesFromCacheDir()`. - * - * @since 151114 Updated to support an arbitrary URL instead of a regex frag. - * - * @param string $url The input URL to convert. This CAN be left empty when necessary. - * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. - * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. - * - * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. - * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. - * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. - * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. - * - * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. - */ -$self->buildCachePathRegex = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - $url = trim((string) $url); - $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag); - $cache_path_regex = ''; // Initialize regex. - - if ($url) { - $flags = CACHE_PATH_NO_SCHEME // Scheme added below. - | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT; - $cache_path = $self->buildCachePath($url, '', '', $flags); // Without the scheme. - $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.preg_quote($cache_path, '/') : ''; - } - return '/^'.$cache_path_regex.$regex_suffix_frag.'/i'; -}; - -/* - * Regex pattern for a call to `deleteFilesFromHostCacheDir()`. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $url The input URL to convert. This CAN be left empty when necessary. - * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. - * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. - * - * @param string $regex_suffix_frag Regex fragment to come after the relative cache/path regex frag. - * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. - * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. - * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. - * - * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`. - */ -$self->buildHostCachePathRegex = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - $url = trim((string) $url); - $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag); - $abs_relative_cache_path_regex = ''; // Initialize regex. - $is_url_domain_mapped = false; // Initialize. - - if ($url) { - if (is_multisite() && $self->canConsiderDomainMapping()) { - // Shortest possible URI; i.e., consider domain mapping. - $url = $self->domainMappingUrlFilter($url); - $is_url_domain_mapped = $url && $self->domainMappingBlogId($url); - } - if ($url && ($url_parts = $self->parseUrl($url)) && !empty($url_parts['host'])) { - $flags = CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST - | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT; - - $host_base_dir_tokens = $self->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/'); - $host_url = rtrim('http://'.$url_parts['host'].$host_base_dir_tokens, '/'); - $host_cache_path = $self->buildCachePath($host_url, '', '', $flags); - - $cache_path = $self->buildCachePath($url, '', '', $flags); - $relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $cache_path); - - $abs_relative_cache_path = isset($relative_cache_path[0]) ? '/'.$relative_cache_path : ''; - $abs_relative_cache_path_regex = isset($abs_relative_cache_path[0]) ? preg_quote($abs_relative_cache_path, '/') : ''; - } - } - return '/^'.$abs_relative_cache_path_regex.$regex_suffix_frag.'/i'; -}; - -/* - * Regex pattern for a call to `deleteFilesFromCacheDir()`. - * - * @since 151114 Improving watered-down regex syntax. - * - * @param string $url The input URL to convert. This CAN be left empty when necessary. - * This may also contain watered-down regex; i.e., `*^$` characters are OK here. - * However, `^$` are discarded, as they are unnecessary in this context. - * - * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. - * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. - * - * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. - * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. - * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. - * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. - * - * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. - */ -$self->buildCachePathRegexFromWcUrl = function ($url, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - $url = trim((string) $url, '^$'); - $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag); - $cache_path_regex = ''; // Initialize regex. - - if ($url) { // After `^$` trimming above. - $flags = CACHE_PATH_ALLOW_WILDCARDS | CACHE_PATH_NO_SCHEME - | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT; - $cache_path = $self->buildCachePath($url, '', '', $flags); // Without the scheme. - $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.$self->wdRegexToActualRegexFrag($cache_path) : ''; - } - return '/^'.$cache_path_regex.$regex_suffix_frag.'/i'; -}; - -/* - * Regex pattern for a call to `deleteFilesFromHostCacheDir()`. - * - * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. - * - * @param string $uris A line-delimited list of URIs. These may contain `*^$` also. - * However, `^$` are discarded, as they are unnecessary in this context. - * - * @param string $regex_suffix_frag Regex fragment to come after each relative cache/path. - * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. - * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. - * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. - * - * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`. - */ -$self->buildHostCachePathRegexFragsFromWcUris = function ($uris, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - $uris = trim((string) $uris); - $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag); - - $_self = $self; // Reference for the closure below. - $flags = CACHE_PATH_ALLOW_WILDCARDS | CACHE_PATH_NO_SCHEME | CACHE_PATH_NO_HOST - | CACHE_PATH_NO_PATH_INDEX | CACHE_PATH_NO_QUV | CACHE_PATH_NO_EXT; - - $host = 'doesnt-matter.foo.bar'; - $host_url = rtrim('http://'.$host, '/'); - $host_cache_path = $self->buildCachePath($host_url, '', '', $flags); - $uri_patterns = array_unique(preg_split('/['."\r\n".']+/', $uris, -1, PREG_SPLIT_NO_EMPTY)); - - foreach ($uri_patterns as $_key => &$_uri_pattern) { - if (($_uri_pattern = trim($_uri_pattern, '^$'))) { - $_cache_path = $_self->buildCachePath($host_url.'/'.trim($_uri_pattern, '/'), '', '', $flags); - $_relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path); - $_uri_pattern = $self->wdRegexToActualRegexFrag($_relative_cache_path); - } - if (!$_uri_pattern) { - unset($uri_patterns[$_key]); // Remove it. - } - } - unset($_key, $_uri_pattern, $_cache_path, $_relative_cache_path); // Housekeeping. - - return $uri_patterns ? '(?:'.implode('|', array_unique($uri_patterns)).')'.$regex_suffix_frag : ''; -}; - -/* - * Regex pattern for a call to `deleteFilesFromCacheDir()`. - * - * @since 151114 Moving this low-level routine into a method of a different name. - * - * @param string $regex_frag A regex fragment. This CAN be left empty when necessary. - * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. - * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. - * - * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. - * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. - * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. - * See also: {@link CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. - * - * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. - */ -$self->assembleCachePathRegex = function ($regex_frag, $regex_suffix_frag = CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) use ($self) { - $regex_frag = (string) $regex_frag; - $regex_suffix_frag = $self->cachePathRegexSuffixFrag($regex_suffix_frag); - - return '/^'.$regex_frag.$regex_suffix_frag.'/i'; -}; diff --git a/src/includes/closures/Shared/ConditionalUtils.php b/src/includes/closures/Shared/ConditionalUtils.php deleted file mode 100644 index 1c85d59..0000000 --- a/src/includes/closures/Shared/ConditionalUtils.php +++ /dev/null @@ -1,358 +0,0 @@ -php_constructs = [ - 'die' => 'die', - 'echo' => 'echo', - 'empty' => 'empty', - 'exit' => 'exit', - 'eval' => 'eval', - 'include' => 'include', - 'include_once' => 'include_once', - 'isset' => 'isset', - 'list' => 'list', - 'require' => 'require', - 'require_once' => 'require_once', - 'return' => 'return', - 'print' => 'print', - 'unset' => 'unset', - '__halt_compiler' => '__halt_compiler', -]; - -/* - * Is AdvancedCache class? - * - * @since 150821 Improving multisite compat. - * - * @return bool `TRUE` if this is the AdvancedCache class. - */ -$self->isAdvancedCache = function () use ($self) { - return $self instanceof AdvancedCache; -}; - -/* - * Is Plugin class? - * - * @since 150821 Improving multisite compat. - * - * @return bool `TRUE` if this is the Plugin class. - */ -$self->isPlugin = function () use ($self) { - return $self instanceof Plugin; -}; - -/* - * Is the current request method `POST`, `PUT` or `DELETE`? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if current request method is `POST`, `PUT` or `DELETE`. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isPostPutDeleteRequest = function () use ($self) { - if (!is_null($is = &$self->staticKey('isPostPutDeleteRequest'))) { - return $is; // Already cached this. - } - if (!empty($_POST)) { - return ($is = true); - } - if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { - if (in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('POST', 'PUT', 'DELETE'), true)) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Does the current request include an uncacheable query string? - * - * @since 151002 Improving Nginx support. - * - * @return boolean True if request includes an uncacheable query string. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->requestContainsUncacheableQueryVars = function () use ($self) { - if (!is_null($is = &$self->staticKey('requestContainsUncacheableQueryVars'))) { - return $is; // Already cached this. - } - if (!empty($_GET) || !empty($_SERVER['QUERY_STRING'])) { - $_get_count = !empty($_GET) ? count($_GET) : 0; - $is_abc_only = $_get_count === 1 && isset($_GET[strtolower(SHORT_NAME).'ABC']); - $is_nginx_q_only = $_get_count === 1 && isset($_GET['q']) && $self->isNginx(); - $is_ac_get_var_true = isset($_GET[strtolower(SHORT_NAME).'AC']) && filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN); - - if (!$is_abc_only && !$is_nginx_q_only && !$is_ac_get_var_true) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Is the current request method is uncacheable? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if current request method is uncacheable. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isUncacheableRequestMethod = function () use ($self) { - if (!is_null($is = &$self->staticKey('isUncacheableRequestMethod'))) { - return $is; // Already cached this. - } - if (!empty($_POST)) { - return ($is = true); - } - if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { - if (!in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('GET'), true)) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Should the current user should be considered a logged-in user? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if current user should be considered a logged-in user. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isLikeUserLoggedIn = function () use ($self) { - if (!is_null($is = &$self->staticKey('isLikeUserLoggedIn'))) { - return $is; // Already cached this. - } - if (defined('SID') && SID) { - return ($is = true); // Session ID. - } - if (empty($_COOKIE)) { - return ($is = false); // No cookies. - } - $regex_logged_in_cookies = '/^'; // Initialize. - - if (defined('LOGGED_IN_COOKIE') && LOGGED_IN_COOKIE) { - $regex_logged_in_cookies .= preg_quote(LOGGED_IN_COOKIE, '/'); - } else { // Use the default hard-coded cookie prefix. - $regex_logged_in_cookies .= 'wordpress_logged_in_'; - } - $regex_logged_in_cookies .= '|comment_author_'; - $regex_logged_in_cookies .= '|wp[_\-]postpass_'; - - $regex_logged_in_cookies .= '/'; // Close regex. - - foreach ($_COOKIE as $_key => $_value) { - if ($_value && preg_match($regex_logged_in_cookies, $_key)) { - return ($is = true); // Like a logged-in user. - } - } unset($_key, $_value); // Housekeeping. - - return ($is = false); -}; - -/* - * Are we in a LOCALHOST environment? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if we are in a LOCALHOST environment. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isLocalhost = function () use ($self) { - if (!is_null($is = &$self->staticKey('isLocalhost'))) { - return $is; // Already cached this. - } - if (defined('LOCALHOST')) { - return ($is = (boolean) LOCALHOST); - } - if (preg_match('/\b(?:localhost|127\.0\.0\.1)\b/i', $self->hostToken())) { - return ($is = true); - } - return ($is = false); -}; - - - -/* - * Is the current request for a feed? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if the current request is for a feed. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isFeed = function () use ($self) { - if (!is_null($is = &$self->staticKey('isFeed'))) { - return $is; // Already cached this. - } - if (isset($_REQUEST['feed'])) { - return ($is = true); - } - if (!empty($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI'])) { - if (preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI'])) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Is a document/string an HTML/XML doc; or no? - * - * @since 150422 Rewrite. - * - * @param string $doc Input string/document to check. - * - * @return boolean True if `$doc` is an HTML/XML doc type. - */ -$self->isHtmlXmlDoc = function ($doc) use ($self) { - $doc = trim((string) $doc); - $doc_hash = sha1($doc); - - if (!is_null($is = &$self->staticKey('isHtmlXmlDoc', $doc_hash))) { - return $is; // Already cached this. - } - if (stripos($doc, '') !== false) { - return ($is = true); - } - if (stripos($doc, 'hasACacheableContentType = function () use ($self) { - if (!is_null($is = &$self->staticKey('hasACacheableContentType'))) { - return $is; // Already cached this. - } - foreach ($self->headersList() as $_key => $_header) { - if (stripos($_header, 'Content-Type:') === 0) { - $content_type = $_header; // Last one. - } - } unset($_key, $_header); // Housekeeping. - - if (isset($content_type[0]) && stripos($content_type, 'html') === false - && stripos($content_type, 'xml') === false && stripos($content_type, GLOBAL_NS) === false) { - return ($is = false); // Do NOT cache data sent by scripts serving other MIME types. - } - return ($is = true); -}; - -/* - * Does the current request have a cacheable HTTP status code? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if the current request has a cacheable HTTP status code. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - * - * @warning Do NOT call upon this method until the end of a script execution. - */ -$self->hasACacheableStatus = function () use ($self) { - if (!is_null($is = &$self->staticKey('hasACacheableStatus'))) { - return $is; // Already cached this. - } - if (($http_status = (string) $self->httpStatus()) && $http_status[0] !== '2' && $http_status !== '404') { - return ($is = false); // A non-2xx & non-404 status code. - } - foreach ($self->headersList() as $_key => $_header) { - if (preg_match('/^(?:Retry\-After\:\s+(?P.+)|Status\:\s+(?P[0-9]+)|HTTP\/[0-9]+(?:\.[0-9]+)?\s+(?P[0-9]+))/i', $_header, $_m)) { - if (!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404') - || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404') - ) { - return ($is = false); // Not a cacheable status. - } - } - } unset($_key, $_header); // Housekeeping. - - return ($is = true); -}; - -/* - * Checks if a PHP extension is loaded up. - * - * @since 150422 Rewrite. - * - * @param string $extension A PHP extension slug (i.e. extension name). - * - * @return boolean `TRUE` if the extension is loaded. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isExtensionLoaded = function ($extension) use ($self) { - $extension = (string) $extension; - - if (!is_null($is = &$self->staticKey('isExtensionLoaded', $extension))) { - return $is; // Already cached this. - } - return ($is = (boolean) extension_loaded($extension)); -}; - -/* - * Is a particular function possible in every way? - * - * @since 150422 Rewrite. - * - * @param string $function A PHP function (or user function) to check. - * - * @return string `TRUE` if the function is possible. - * - * @note This checks (among other things) if the function exists and that it's callable. - * It also checks the currently configured `disable_functions` and `suhosin.executor.func.blacklist`. - */ -$self->functionIsPossible = function ($function) use ($self) { - $function = (string) $function; - - if (!is_null($is = &$self->staticKey('functionIsPossible', $function))) { - return $is; // Already cached this. - } - if (is_null($disabled_functions = &$self->staticKey('functionIsPossible_disabled_functions'))) { - $disabled_functions = array(); // Initialize disabled/blacklisted functions. - - if (($disable_functions = trim(ini_get('disable_functions')))) { - $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($disable_functions), -1, PREG_SPLIT_NO_EMPTY)); - } - if (($blacklist_functions = trim(ini_get('suhosin.executor.func.blacklist')))) { - $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($blacklist_functions), -1, PREG_SPLIT_NO_EMPTY)); - } - if(filter_var(ini_get('suhosin.executor.disable_eval'), FILTER_VALIDATE_BOOLEAN)) { - $disabled_functions = array_merge($disabled_functions, array('eval')); - } - } - if (!function_exists($function) || !is_callable($function)) { - if(!in_array($function, $self->php_constructs, true)) { // A language construct - return ($is = false); // Not possible. - } - } - if ($disabled_functions && in_array(strtolower($function), $disabled_functions, true)) { - return ($is = false); // Not possible. - } - return ($is = true); -}; diff --git a/src/includes/closures/Shared/DomainMappingUtils.php b/src/includes/closures/Shared/DomainMappingUtils.php deleted file mode 100644 index bd8382d..0000000 --- a/src/includes/closures/Shared/DomainMappingUtils.php +++ /dev/null @@ -1,264 +0,0 @@ -canConsiderDomainMapping = function () use ($self) { - if (!is_null($can = &$self->staticKey('canConsiderDomainMapping'))) { - return $can; // Already cached this. - } - if (!$self->isAdvancedCache() && is_multisite() && $self->hostBaseToken() === '/' - && defined('SUNRISE_LOADED') && SUNRISE_LOADED && !empty($GLOBALS['dm_domain'])) { - return ($can = true); // Can consider. - } - return ($can = false); // Cannot consider. -}; - -/* - * Domain mapping? - * - * @since 150821 Improving multisite compat. - * - * @return integer Domain mapping ID; else `0` (false). - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isDomainMapping = function () use ($self) { - if (!is_null($is = &$self->staticKey('isDomainMapping'))) { - return $is; // Already cached this. - } - if (!$self->isAdvancedCache() && is_multisite() && $self->canConsiderDomainMapping() - && defined('DOMAIN_MAPPING') && DOMAIN_MAPPING && !empty($GLOBALS['domain_mapping_id'])) { - return ($is = (integer) $GLOBALS['domain_mapping_id']); // Blog ID. - } - return ($is = 0); // Not domain mapping. -}; - -/* - * Filters a URL in order to apply domain mapping. - * - * @since 150821 Improving multisite compat. - * - * @param string $url The input URL to filter. - * - * @return string The filtered URL; else the original URL. - * - * @note The return value of this function is NOT cached, but inner portions are. - */ -$self->domainMappingUrlFilter = function ($url) use ($self) { - $original_url = (string) $url; // Preserve. - $url = trim((string) $url); - - if (!is_multisite() || !$self->canConsiderDomainMapping()) { - return $original_url; // Not possible. - } - if (!$url || !($url_parts = $self->parseUrl($url))) { - return $original_url; // Not possible. - } - if (empty($url_parts['host'])) { - return $original_url; // Not possible. - } - $blog_domain = strtolower($url_parts['host']); // In the unfiltered URL. - $blog_path = $self->hostDirToken(false, false, !empty($url_parts['path']) ? $url_parts['path'] : '/'); - - if (!($blog_id = (integer) get_blog_id_from_url($blog_domain, $blog_path))) { - return $original_url; // Not possible. - } - if (!($domain = $self->domainMappingBlogDomain($blog_id)) || $domain === $blog_domain) { - return $original_url; // Not applicable. - } - $url_parts['host'] = $domain; // Filter the URL now. - if (!empty($url_parts['path']) && $url_parts['path'] !== '/') { - if (($host_base_dir_tokens = trim($self->hostBaseDirTokens(false, false, $url_parts['path']), '/'))) { - $url_parts['path'] = preg_replace('/^\/'.preg_quote($host_base_dir_tokens, '/').'(\/|$)/i', '${1}', $url_parts['path']); - } - } - return ($url = $self->unParseUrl($url_parts)); -}; - -/* - * Filters a URL in order to remove domain mapping. - * - * @since 150821 Improving multisite compat. - * - * @param string $url The input URL to filter. - * - * @return string The filtered URL; else the original URL. - * - * @note The return value of this function is NOT cached, but inner portions are. - */ -$self->domainMappingReverseUrlFilter = function ($url) use ($self) { - $original_url = (string) $url; // Preserve. - $url = trim((string) $url); - - if (!is_multisite() || !$self->canConsiderDomainMapping()) { - return $original_url; // Not possible. - } - if (!$url || !($url_parts = $self->parseUrl($url))) { - return $original_url; // Not possible. - } - if (empty($url_parts['host'])) { - return $original_url; // Not possible. - } - if (!($blog_id = $self->domainMappingBlogId('', $url_parts['host']))) { - return $original_url; // No a domain in the map. - } - if (!($blog_details = $self->blogDetails($blog_id))) { - return $original_url; // Not possible. - } - $url_parts['host'] = $blog_details->domain; // Filter the URL now. - if (($host_base_dir_tokens = trim($self->hostBaseDirTokens(false, false, $blog_details->path), '/'))) { - $url_parts['path'] = '/'.$host_base_dir_tokens.'/'.ltrim(@$url_parts['path'], '/'); - } - return ($url = $self->unParseUrl($url_parts)); -}; - -/* - * Converts a host into a mapped blog ID. - * - * @since 150821 Improving multisite compat. - * - * @param string $url URL containing the domain to convert. - * @param string $domain The domain to convert. Override URL is provided. - * - * @return integer The mapped blog ID; else `0` on failure. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->domainMappingBlogId = function ($url = '', $domain = '') use ($self) { - $domain = (string) $domain; - $url = $domain ? '' : (string) $url; - - if (!is_multisite() || !$self->canConsiderDomainMapping()) { - return 0; // Not possible/applicable. - } - if ($url === 'network' || $domain === 'network') { - $domain = (string) get_current_site()->domain; - } - if (!$domain && $url && $url !== 'network') { - $domain = $self->parseUrl($url, PHP_URL_HOST); - } - if (!$url && !$domain && ($blog_details = $self->blogDetails())) { - $domain = $blog_details->domain; - } - $domain = strtolower(preg_replace('/^www\./i', '', $domain)); - - if (!$domain || strpos($domain, '.') === false) { - return 0; // Not possible. - } - if (!is_null($blog_id = &$self->staticKey('domainMappingBlogId', $domain))) { - return $blog_id; // Already cached this. - } - $wpdb = $self->wpdb(); // WordPress database class. - $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. - $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? - - if (!$enforcing_primary_domain) { - $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC, `active` DESC LIMIT 1'); - } else { - $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') AND `active` = \'1\' ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1'); - } - $wpdb->suppress_errors($suppressing_errors); // Restore. - - return ($blog_id = (integer) $blog_id); -}; - -/* - * Converts a blog ID into a mapped domain. - * - * @since 150821 Improving multisite compat. - * - * @param integer $blog_id The blog ID. - * - * @param boolean $fallback Fallback on blog's domain? - * - * @return string The mapped domain, else an empty string. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->domainMappingBlogDomain = function ($blog_id = 0, $fallback = false) use ($self) { - if (!is_multisite() || !$self->canConsiderDomainMapping()) { - return ''; // Not possible/applicable. - } - if (($blog_id = (integer) $blog_id) < 0) { - $blog_id = (integer) get_current_site()->blog_id; - } - if (!$blog_id) { - $blog_id = (integer) get_current_blog_id(); - } - if (!$blog_id || $blog_id < 0) { - return ''; // Not possible. - } - if (!is_null($domain = &$self->staticKey('domainMappingBlogDomain', $blog_id))) { - return $domain; // Already cached this. - } - $wpdb = $self->wpdb(); // WordPress database class. - $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. - $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? - - if (!$enforcing_primary_domain) { - if ($self->isDomainMapping() === $blog_id) { - $domain = $self->hostToken(); - $domain = preg_replace('/^www\./i', '', $domain); - $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1'); - } elseif (($domains = $self->domainMappingBlogDomains($blog_id))) { - $domain = $domains[0]; // Use the first of all possible domains. - } - } else { // A single primary domain in this case; i.e., `active` = primary. - $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\' LIMIT 1'); - } - if (!$domain && $fallback && ($blog_details = $self->blogDetails($blog_id))) { - $domain = $blog_details->domain; // Use original domain. - } - $wpdb->suppress_errors($suppressing_errors); // Restore. - - return ($domain = strtolower((string) $domain)); -}; - -/* - * Converts a blog ID into mapped domains (plural). - * - * @since 150821 Improving multisite compat. - * - * @param integer $blog_id The blog ID. - * - * @return array Mapped domains; else an empty array. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->domainMappingBlogDomains = function ($blog_id = 0) use ($self) { - if (!is_multisite() || !$self->canConsiderDomainMapping()) { - return array(); // Not possible/applicable. - } - if (($blog_id = (integer) $blog_id) < 0) { - $blog_id = (integer) get_current_site()->blog_id; - } - if (!$blog_id) { - $blog_id = (integer) get_current_blog_id(); - } - if (!$blog_id || $blog_id < 0) { - return array(); // Not possible. - } - if (!is_null($domains = &$self->staticKey('domainMappingBlogDomains', $blog_id))) { - return $domains; // Already cached this. - } - $wpdb = $self->wpdb(); // WordPress database class. - $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. - $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? - - if (!$enforcing_primary_domain) { // Not enforcing a primary domain, so let's pull all of the domains. - $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' ORDER BY `active` DESC'); - } else { // Primary domains in this case; i.e., `active` = primary. - $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\''); - } - $wpdb->suppress_errors($suppressing_errors); // Restore. - - return ($domains = array_unique(array_map('strtolower', (array) $domains))); -}; diff --git a/src/includes/closures/Shared/EscapeUtils.php b/src/includes/closures/Shared/EscapeUtils.php deleted file mode 100644 index 16cc249..0000000 --- a/src/includes/closures/Shared/EscapeUtils.php +++ /dev/null @@ -1,17 +0,0 @@ - `1`. - * - * @return string Escaped string; e.g. `Raam\'s the lead developer`. - */ -$self->escSq = function ($string, $times = 1) use ($self) { - return str_replace("'", str_repeat('\\', abs($times))."'", (string) $string); -}; diff --git a/src/includes/closures/Shared/FsUtils.php b/src/includes/closures/Shared/FsUtils.php deleted file mode 100644 index bbca2c4..0000000 --- a/src/includes/closures/Shared/FsUtils.php +++ /dev/null @@ -1,323 +0,0 @@ -nDirSeps = function ($dir_file, $allow_trailing_slash = false) use ($self) { - $dir_file = (string) $dir_file; - - if (!isset($dir_file[0])) { - return ''; // Catch empty string. - } - if (strpos($dir_file, '://' !== false)) { - if (preg_match('/^(?P[a-zA-Z0-9]+)\:\/\//', $dir_file, $stream_wrapper)) { - $dir_file = preg_replace('/^(?P[a-zA-Z0-9]+)\:\/\//', '', $dir_file); - } - } - if (strpos($dir_file, ':' !== false)) { - if (preg_match('/^(?P[a-zA-Z])\:[\/\\\\]/', $dir_file)) { - $dir_file = preg_replace_callback('/^(?P[a-zA-Z])\:[\/\\\\]/', create_function('$m', 'return strtoupper($m[0]);'), $dir_file); - } - } - $dir_file = preg_replace('/\/+/', '/', str_replace(array(DIRECTORY_SEPARATOR, '\\', '/'), '/', $dir_file)); - $dir_file = ($allow_trailing_slash) ? $dir_file : rtrim($dir_file, '/'); // Strip trailing slashes. - - if (!empty($stream_wrapper[0])) { - $dir_file = strtolower($stream_wrapper[0]).$dir_file; - } - return $dir_file; // Normalized now. -}; - -/* - * Acquires system tmp directory path. - * - * @since 150422 Rewrite. - * - * @return string System tmp directory path; else an empty string. - */ -$self->getTmpDir = function () use ($self) { - if (!is_null($dir = &$self->staticKey('getTmpDir'))) { - return $dir; // Already cached this. - } - $possible_dirs = array(); // Initialize. - - if (defined('WP_TEMP_DIR')) { - $possible_dirs[] = (string) WP_TEMP_DIR; - } - if ($self->functionIsPossible('sys_get_temp_dir')) { - $possible_dirs[] = (string) sys_get_temp_dir(); - } - $possible_dirs[] = (string) ini_get('upload_tmp_dir'); - - if (!empty($_SERVER['TEMP'])) { - $possible_dirs[] = (string) $_SERVER['TEMP']; - } - if (!empty($_SERVER['TMPDIR'])) { - $possible_dirs[] = (string) $_SERVER['TMPDIR']; - } - if (!empty($_SERVER['TMP'])) { - $possible_dirs[] = (string) $_SERVER['TMP']; - } - if (stripos(PHP_OS, 'win') === 0) { - $possible_dirs[] = 'C:/Temp'; - } - if (stripos(PHP_OS, 'win') !== 0) { - $possible_dirs[] = '/tmp'; - } - if (defined('WP_CONTENT_DIR')) { - $possible_dirs[] = (string) WP_CONTENT_DIR; - } - foreach ($possible_dirs as $_key => $_dir) { - if (($_dir = trim((string) $_dir)) && @is_dir($_dir) && @is_writable($_dir)) { - return ($dir = $self->nDirSeps($_dir)); - } - } - unset($_key, $_dir); // Housekeeping. - - return ($dir = ''); -}; - -/* - * Finds absolute server path to `/wp-config.php` file. - * - * @since 150422 Rewrite. - * - * @return string Absolute server path to `/wp-config.php` file; - * else an empty string if unable to locate the file. - */ -$self->findWpConfigFile = function () use ($self) { - if (!is_null($file = &$self->staticKey('findWpConfigFile'))) { - return $file; // Already cached this. - } - $file = ''; // Initialize. - - if (is_file($abspath_wp_config = ABSPATH.'wp-config.php')) { - $file = $abspath_wp_config; - } elseif (is_file($dirname_abspath_wp_config = dirname(ABSPATH).'/wp-config.php')) { - $file = $dirname_abspath_wp_config; - } - return $file; -}; - -/* - * Adds a tmp name suffix to a directory/file path. - * - * @since 150422 Rewrite. - * - * @param string $dir_file An input directory or file path. - * - * @return string The original `$dir_file` with a tmp name suffix. - */ -$self->addTmpSuffix = function ($dir_file) use ($self) { - $dir_file = (string) $dir_file; - $dir_file = rtrim($dir_file, DIRECTORY_SEPARATOR.'\\/'); - - return $dir_file.'-'.str_replace('.', '', uniqid('', true)).'-tmp'; -}; - -/* - * Recursive directory iterator based on a regex pattern. - * - * @since 150422 Rewrite. - * - * @param string $dir An absolute server directory path. - * @param string $regex A regex pattern; compares to each full file path. - * - * @return \RegexIterator Navigable with {@link \foreach()}; where each item - * is a {@link \RecursiveDirectoryIterator}. - */ -$self->dirRegexIteration = function ($dir, $regex = '') use ($self) { - $dir = (string) $dir; - $regex = (string) $regex; - - $dir_iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS); - $iterator_iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST); - - if ($regex && $regex !== '/.*/' && $regex !== '/.+/') { // Apply regex filter? - // @TODO Optimize calls to this method in order to avoid the regex iterator when not necessary. - return new \RegexIterator($iterator_iterator, $regex, \RegexIterator::MATCH, \RegexIterator::USE_KEY); - } - return $iterator_iterator; // Iterate everything. -}; - -/* - * Abbreviated byte notation for file sizes. - * - * @since 151002 Adding a few statistics. - * - * @param float $bytes File size in bytes. A (float) value. - * @param integer $precision Number of decimals to use. - * - * @return string Byte notation. - */ -$self->bytesAbbr = function ($bytes, $precision = 2) use ($self) { - $bytes = max(0.0, (float) $bytes); - $precision = max(0, (integer) $precision); - $units = array('bytes', 'kbs', 'MB', 'GB', 'TB'); - - $power = floor(($bytes ? log($bytes) : 0) / log(1024)); - $abbr_bytes = round($bytes / pow(1024, $power), $precision); - $abbr = $units[min($power, count($units) - 1)]; - - if ($abbr_bytes === (float) 1 && $abbr === 'bytes') { - $abbr = 'byte'; // Quick fix. - } elseif ($abbr_bytes === (float) 1 && $abbr === 'kbs') { - $abbr = 'kb'; // Quick fix. - } - return $abbr_bytes.' '.$abbr; -}; - -/* - * Converts an abbreviated byte notation into bytes. - * - * @since 151002 Adding a few statistics. - * - * @param string $string A string value in byte notation. - * - * @return float A float indicating the number of bytes. - */ -$self->abbrBytes = function ($string) use ($self) { - $string = (string) $string; - $regex = '/^(?P[0-9\.]+)\s*(?Pbytes|byte|kbs|kb|k|mb|m|gb|g|tb|t)$/i'; - - if (!preg_match($regex, $string, $_m)) { - return (float) 0; - } - $value = (float) $_m['value']; - $modifier = strtolower($_m['modifier']); - unset($_m); // Housekeeping. - - switch ($modifier) { - case 't': - case 'tb': - $value *= 1024; - // Fall through. - case 'g': - case 'gb': - $value *= 1024; - // Fall through. - case 'm': - case 'mb': - $value *= 1024; - // Fall through. - case 'k': - case 'kb': - case 'kbs': - $value *= 1024; - } - return (float) $value; -}; - -/* - * Directory stats. - * - * @since 151002 Adding a few statistics. - * - * @param string $dir An absolute server directory path. - * @param string $regex A regex pattern; compares to each full file path. - * @param boolean $include_paths Include array of all scanned file paths? - * @param boolean $check_disk Also check disk statistics? - * @param boolean $no_cache Do not read/write cache? - * - * @return array Directory stats. - */ -$self->getDirRegexStats = function ($dir, $regex = '', $include_paths = false, $check_disk = true, $no_cache = false) use ($self) { - $dir = (string) $dir; // Force string. - $cache_keys = array($dir, $regex, $include_paths, $check_disk); - if (!$no_cache && !is_null($stats = &$self->staticKey('getDirRegexStats', $cache_keys))) { - return $stats; // Already cached this. - } - $stats = array( - 'total_size' => 0, - 'total_resources' => 0, - 'total_links_files' => 0, - - 'total_links' => 0, - 'link_subpaths' => array(), - - 'total_files' => 0, - 'file_subpaths' => array(), - - 'total_dirs' => 0, - 'dir_subpaths' => array(), - - 'disk_total_space' => 0, - 'disk_free_space' => 0, - ); - if (!$dir || !is_dir($dir)) { - return $stats; // Not possible. - } - $short_name_lc = strtolower(SHORT_NAME); // Once only. - - foreach ($self->dirRegexIteration($dir, $regex) as $_resource) { - $_resource_sub_path = $_resource->getSubpathname(); - $_resource_basename = basename($_resource_sub_path); - - if ($_resource_basename === '.DS_Store') { - continue; // Ignore `.htaccess`. - } - if ($_resource_basename === '.htaccess') { - continue; // Ignore `.htaccess`. - } - if (stripos($_resource_sub_path, $short_name_lc.'-') === 0) { - continue; // Ignore [SHORT_NAME] files in base. - } - switch ($_resource->getType()) { // `link`, `file`, `dir`. - case 'link': - if ($include_paths) { - $stats['link_subpaths'][] = $_sub_path; - } - ++$stats['total_resources']; - ++$stats['total_links_files']; - ++$stats['total_links']; - - break; // Break switch. - - case 'file': - if ($include_paths) { - $stats['file_subpaths'][] = $_sub_path; - } - $stats['total_size'] += $_resource->getSize(); - ++$stats['total_resources']; - ++$stats['total_links_files']; - ++$stats['total_files']; - - break; // Break switch. - - case 'dir': - if ($include_paths) { - $stats['dir_subpaths'][] = $_sub_path; - } - ++$stats['total_resources']; - ++$stats['total_dirs']; - - break; // Break switch. - } - } - unset($_resource, $_resource_sub_path, $_resource_basename); // Housekeeping. - - if ($check_disk) { // Check disk also? - $stats['disk_total_space'] = disk_total_space($dir); - $stats['disk_free_space'] = disk_free_space($dir); - } - return $stats; -}; - -/* - * Apache `.htaccess` rules that deny public access to the contents of a directory. - * - * @since 150422 Rewrite. - * - * @var string `.htaccess` fules. - */ -$self->htaccess_deny = "\n\tRequire all denied\n\n\n\tdeny from all\n"; diff --git a/src/includes/closures/Shared/HookUtils.php b/src/includes/closures/Shared/HookUtils.php deleted file mode 100644 index aa57bf2..0000000 --- a/src/includes/closures/Shared/HookUtils.php +++ /dev/null @@ -1,249 +0,0 @@ -hooks = array(); - -/* - * Assigns an ID to each callable attached to a hook/filter. - * - * @since 150422 Rewrite. - * - * @param string|callable|mixed $function A string or a callable. - * - * @return string Hook ID for the given `$function`. - * - * @throws \Exception If the hook/function is invalid (i.e. it's not possible to generate an ID). - */ -$self->hookId = function ($function) use ($self) { - if (is_string($function)) { - return $function; - } - if (is_object($function)) { - $function = array($function, ''); - } else { - $function = (array) $function; - } - if (isset($function[0], $function[1])) { - if (is_object($function[0])) { - return spl_object_hash($function[0]).$function[1]; - } elseif (is_string($function[0])) { - return $function[0].'::'.$function[1]; - } - } - throw new \Exception(__('Invalid hook.', 'comet-cache')); -}; - -/* - * Adds a new hook (works with both actions & filters). - * - * @since 150422 Rewrite. - * - * @param string $hook The name of a hook to attach to. - * @param string|callable|mixed $function A string or a callable. - * @param integer $priority Hook priority; defaults to `10`. - * @param integer $accepted_args Max number of args that should be passed to the `$function`. - * - * @return boolean This always returns a `TRUE` value. - */ -$self->addHook = function ($hook, $function, $priority = 10, $accepted_args = 1) use ($self) { - $hook = (string) $hook; - if (stripos($hook, 'zencache') === 0) { - $hook = GLOBAL_NS.substr($hook, strlen('zencache')); - } - $priority = (integer) $priority; - $accepted_args = max(0, (integer) $accepted_args); - $hook_id = $self->hookId($function); - - $self->hooks[$hook][$priority][$hook_id] = array( - 'function' => $function, - 'accepted_args' => $accepted_args, - ); - return true; // Always returns true. -}; - -/* - * Adds a new action hook. - * - * @since 150422 Rewrite. - * - * @return boolean This always returns a `TRUE` value. - */ -$self->addAction = function () use ($self) { - return call_user_func_array(array($self, 'addHook'), func_get_args()); -}; -$self->add_action = $self->addAction; // Back compat. - -/* - * Adds a new filter. - * - * @since 150422 Rewrite. - * - * @return boolean This always returns a `TRUE` value. - */ -$self->addFilter = function () use ($self) { - return call_user_func_array(array($self, 'addHook'), func_get_args()); -}; -$self->add_filter = $self->addFilter; // Back compat. - -/* - * Removes a hook (works with both actions & filters). - * - * @since 150422 Rewrite. - * - * @param string $hook The name of a hook to remove. - * @param string|callable|mixed $function A string or a callable. - * @param integer $priority Hook priority; defaults to `10`. - * - * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason. - */ -$self->removeHook = function ($hook, $function, $priority = 10) use ($self) { - $hook = (string) $hook; - if (stripos($hook, 'zencache') === 0) { - $hook = GLOBAL_NS.substr($hook, strlen('zencache')); - } - $priority = (integer) $priority; - $hook_id = $self->hookId($function); - - if (!isset($self->hooks[$hook][$priority][$hook_id])) { - return false; // Nothing to remove. - } - unset($self->hooks[$hook][$priority][$hook_id]); - - if (!$self->hooks[$hook][$priority]) { - unset($self->hooks[$hook][$priority]); - } - return true; // Existed before it was removed. -}; - -/* - * Removes an action. - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason. - */ -$self->removeAction = function () use ($self) { - return call_user_func_array(array($self, 'removeHook'), func_get_args()); -}; - -/* - * Removes a filter. - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if removed; else `FALSE` if not removed for any reason. - */ -$self->removeFilter = function () use ($self) { - return call_user_func_array(array($self, 'removeHook'), func_get_args()); -}; - -/* - * Runs any callables attached to an action. - * - * @since 150422 Rewrite. - * - * @param string $hook The name of an action hook. - */ -$self->doAction = function ($hook) use ($self) { - $hook = (string) $hook; - if (empty($self->hooks[$hook])) { - return; // No hooks. - } - $hook_actions = $self->hooks[$hook]; - $args = func_get_args(); - ksort($hook_actions); - - foreach ($hook_actions as $_hook_action) { - foreach ($_hook_action as $_action) { - if (!isset($_action['function'], $_action['accepted_args'])) { - continue; // Not a valid filter in this case. - } - call_user_func_array($_action['function'], array_slice($args, 1, $_action['accepted_args'])); - } - } - unset($_hook_action, $_action); // Housekeeping. -}; - -/* - * Runs any callables attached to a filter. - * - * @since 150422 Rewrite. - * - * @param string $hook The name of a filter hook. - * @param mixed $value The value to filter. - * - * @return mixed The filtered `$value`. - */ -$self->applyFilters = function ($hook, $value) use ($self) { - $hook = (string) $hook; - if (empty($self->hooks[$hook])) { - return $value; // No hooks. - } - $hook_filters = $self->hooks[$hook]; - $args = func_get_args(); - ksort($hook_filters); - - foreach ($hook_filters as $_hook_filter) { - foreach ($_hook_filter as $_filter) { - if (!isset($_filter['function'], $_filter['accepted_args'])) { - continue; // Not a valid filter in this case. - } - $args[1] = $value; // Continously update the argument `$value`. - $value = call_user_func_array($_filter['function'], array_slice($args, 1, $_filter['accepted_args'])); - } - } - unset($_hook_filter, $_filter); // Housekeeping. - - return $value; // With applied filters. -}; - -/* - * Does an action w/ back compat. for ZenCache. - * - * @since 150422 Rewrite. - * - * @param string $hook The hook to apply. - */ -$self->doWpAction = function ($hook) use ($self) { - $hook = (string) $hook; - $args = func_get_args(); - call_user_func_array('do_action', $args); - - if (stripos($hook, GLOBAL_NS) === 0) { - $zencache_filter = 'zencache'.substr($hook, strlen(GLOBAL_NS)); - $zencache_args = $args; // Use a copy of the args. - $zencache_args[0] = $zencache_filter; - call_user_func_array('do_action', $zencache_args); - } -}; - -/* - * Applies filters w/ back compat. for ZenCache. - * - * @since 150422 Rewrite. - * - * @param string $hook The hook to apply. - * - * @return mixed The filtered value. - */ -$self->applyWpFilters = function ($hook) use ($self) { - $hook = (string) $hook; - $args = func_get_args(); - $value = call_user_func_array('apply_filters', $args); - - if (stripos($hook, GLOBAL_NS) === 0) { - $zencache_hook = 'zencache'.substr($hook, strlen(GLOBAL_NS)); - $zencache_args = $args; // Use a copy of the args. - $zencache_args[0] = $zencache_hook; - $zencache_args[1] = $value; // Filtered value. - $value = call_user_func_array('apply_filters', $zencache_args); - } - return $value; // Filtered value. -}; diff --git a/src/includes/closures/Shared/HttpUtils.php b/src/includes/closures/Shared/HttpUtils.php deleted file mode 100644 index f7ddd8a..0000000 --- a/src/includes/closures/Shared/HttpUtils.php +++ /dev/null @@ -1,166 +0,0 @@ -httpProtocol = function () use ($self) { - if (!is_null($protocol = &$self->staticKey('httpProtocol'))) { - return $protocol; // Already cached this. - } - if (!empty($_SERVER['SERVER_PROTOCOL']) && is_string($_SERVER['SERVER_PROTOCOL'])) { - $protocol = strtoupper($_SERVER['SERVER_PROTOCOL']); - } - if (!$protocol || stripos($protocol, 'HTTP/') !== 0) { - $protocol = 'HTTP/1.0'; // Default value. - } - return $protocol; -}; - -/* - * PHP {@link headers_list()} + HTTP status. - * - * @since 150422 Rewrite. - * - * @return array PHP {@link headers_list()} + HTTP status. - * - * @warning Do NOT call until end of script execution. - */ -$self->headersList = function () use ($self) { - if (!is_null($headers = &$self->staticKey('headersList'))) { - return $headers; // Already cached this. - } - $headers = headers_list(); // Lacks status. - - if (($status = (string) $self->httpStatus())) { - array_unshift($headers, $self->httpProtocol().' '.$status); - } - return $headers; -}; - -/* - * PHP {@link headers_list()} + HTTP status. - * - * @since 150422 Rewrite. - * - * @return array PHP {@link headers_list()} + HTTP status. - * - * @warning Do NOT call until end of script execution. - */ -$self->cacheableHeadersList = function () use ($self) { - if (!is_null($headers = &$self->staticKey('cacheableHeadersList'))) { - return $headers; // Already cached this. - } - $headers = headers_list(); // Lacks status. - - $cacheable_headers = array( - 'Access-Control-Allow-Origin', - 'Accept-Ranges', - 'Age', - 'Allow', - 'Cache-Control', - 'Connection', - 'Content-Encoding', - 'Content-Language', - 'Content-Length', - 'Content-Location', - 'Content-MD5', - 'Content-Disposition', - 'Content-Range', - 'Content-Type', - 'Date', - 'ETag', - 'Expires', - 'Last-Modified', - 'Link', - 'Location', - 'P3P', - 'Pragma', - 'Proxy-Authenticate', - 'Refresh', - 'Retry-After', - 'Server', - 'Status', - 'Strict-Transport-Security', - 'Trailer', - 'Transfer-Encoding', - 'Upgrade', - 'Vary', - 'Via', - 'Warning', - 'WWW-Authenticate', - 'X-Frame-Options', - 'Public-Key-Pins', - 'X-XSS-Protection', - 'Content-Security-Policy', - 'X-Content-Security-Policy', - 'X-WebKit-CSP', - 'X-Content-Type-Options', - 'X-Powered-By', - 'X-UA-Compatible', - ); - $cacheable_headers = array_map('strtolower', $cacheable_headers); - - foreach ($headers as $_key => $_header) { - $_header = strtolower((string) strstr($_header, ':', true)); - if (!$_header || !in_array($_header, $cacheable_headers, true)) { - unset($headers[$_key]); - } - } - unset($_key, $_header); // Housekeeping. - - if (($status = (string) $self->httpStatus())) { - array_unshift($headers, $self->httpProtocol().' '.$status); - } - return $headers; -}; - -/* - * HTTP status code. - * - * @since 150422 Rewrite. - * - * @return integer HTTP status code. - * - * @warning Do NOT call until end of script execution. - * - * @note Automatically updates HTTP status-related flags. - */ -$self->httpStatus = function () use ($self) { - if (!is_null($status = &$self->staticKey('httpStatus'))) { - return $status; // Already cached this. - } - $status = 0; // Initialize. - $has_property_is_404 = property_exists($self, 'is_404'); - $has_property_http_status = property_exists($self, 'http_status'); - - if ($has_property_is_404 && $self->{'is_404'}) { - $status = 404; // WordPress said so. - } elseif ($self->functionIsPossible('http_response_code') && ($code = (integer) http_response_code())) { - $status = (integer) $code; // {@link \http_response_code()} available since PHP v5.4. - } elseif ($has_property_http_status && (integer) $self->{'http_status'}) { - $status = (integer) $self->{'http_status'}; // {@link \status_header()} filter. - } - if ($status && $has_property_http_status) { - $self->{'http_status'} = $status; // Prefer over {@link status_header()}. - } - if ($status === 404 && $has_property_is_404) { - $self->{'is_404'} = true; // Prefer over {@link is_404()}. - } - return $status; -}; -/* -* Sends no-cache headers. -* -* @since 151220 Enhancing no-cache headers. -*/ -$self->sendNoCacheHeaders = function() use($self) { - header_remove('Last-Modified'); - header('Expires: Wed, 11 Jan 1984 05:00:00 GMT'); - header('Cache-Control: no-cache, must-revalidate, max-age=0'); - header('Pragma: no-cache'); -}; diff --git a/src/includes/closures/Shared/I18nUtils.php b/src/includes/closures/Shared/I18nUtils.php deleted file mode 100644 index f352e05..0000000 --- a/src/includes/closures/Shared/I18nUtils.php +++ /dev/null @@ -1,44 +0,0 @@ -i18nFiles = function ($counter) use ($self) { - $counter = (integer) $counter; - return sprintf(_n('%1$s file', '%1$s files', $counter, 'comet-cache'), $counter); -}; - -/* - * `X directory` or `X directories`, translated w/ singlular/plural context. - * - * @since 150422 Rewrite. - * - * @param integer $counter Total directories; i.e. the counter. - * - * @return string The phrase `X directory` or `X directories`. - */ -$self->i18nDirs = function ($counter) use ($self) { - $counter = (integer) $counter; - return sprintf(_n('%1$s directory', '%1$s directories', $counter, 'comet-cache'), $counter); -}; - -/* - * `X file/directory` or `X files/directories`, translated w/ singlular/plural context. - * - * @since 150422 Rewrite. - * - * @param integer $counter Total files/directories; i.e. the counter. - * - * @return string The phrase `X file/directory` or `X files/directories`. - */ -$self->i18nFilesDirs = function ($counter) use ($self) { - $counter = (integer) $counter; - return sprintf(_n('%1$s file/directory', '%1$s files/directories', $counter, 'comet-cache'), $counter); -}; diff --git a/src/includes/closures/Shared/IpAddrUtils.php b/src/includes/closures/Shared/IpAddrUtils.php deleted file mode 100644 index 06ad808..0000000 --- a/src/includes/closures/Shared/IpAddrUtils.php +++ /dev/null @@ -1,83 +0,0 @@ -currentIp = function () use ($self) { - if (!is_null($ip = &$self->staticKey('currentIp'))) { - return $ip; // Already cached this. - } - $sources = array( - 'HTTP_CF_CONNECTING_IP', - 'HTTP_CLIENT_IP', - 'HTTP_X_FORWARDED_FOR', - 'HTTP_X_FORWARDED', - 'HTTP_X_CLUSTER_CLIENT_IP', - 'HTTP_FORWARDED_FOR', - 'HTTP_FORWARDED', - 'HTTP_VIA', - 'REMOTE_ADDR', - ); - $sources = $self->applyFilters(GLOBAL_NS.'\\share::current_ip_sources', $sources); - $sources = $self->applyFilters(GLOBAL_NS.'_current_ip_sources', $sources); - - $prioritize_remote_addr = false; // Off by default; can be filtered however. - $prioritize_remote_addr = $self->applyFilters(GLOBAL_NS.'\\share::current_ip_prioritize_remote_addr', $prioritize_remote_addr); - $prioritize_remote_addr = $self->applyFilters(GLOBAL_NS.'_current_ip_prioritize_remote_addr', $prioritize_remote_addr); - - if (!empty($_SERVER['REMOTE_ADDR']) && $prioritize_remote_addr) { - if (($_valid_public_ip = $self->validPublicIp((string) $_SERVER['REMOTE_ADDR']))) { - return ($ip = $_valid_public_ip); - } - unset($_valid_public_ip); // Housekeeping. - } - foreach ($sources as $_key => $_source) { - if (!empty($_SERVER[$_source])) { - if (($_valid_public_ip = $self->validPublicIp((string) $_SERVER[$_source]))) { - return ($ip = $_valid_public_ip); - } - } - unset($_key, $_source, $_valid_public_ip); // Housekeeping. - } - if (!empty($_SERVER['REMOTE_ADDR'])) { - return ($ip = strtolower((string) $_SERVER['REMOTE_ADDR'])); - } - return ($ip = 'unknown'); // Not possible. -}; - -/* - * Gets a valid/public IP address. - * - * @since 150422 Rewrite. - * - * @param string $list_of_possible_ips A single IP, or a comma-delimited list of IPs. - * - * @return string A valid/public IP address (if one is found), else an empty string. - * - * @note This supports both IPv4 and IPv6 addresses. - * @note See my tests against this here: http://3v4l.org/fVWUp - */ -$self->validPublicIp = function ($list_of_possible_ips) use ($self) { - if (!$list_of_possible_ips || !is_string($list_of_possible_ips)) { - return ''; // Empty or invalid data. - } - if (!($list_of_possible_ips = trim($list_of_possible_ips))) { - return ''; // Not possible; i.e., empty string. - } - foreach (preg_split('/[\s;,]+/', $list_of_possible_ips, -1, PREG_SPLIT_NO_EMPTY) as $_key => $_possible_ip) { - if (($_valid_public_ip = filter_var(strtolower($_possible_ip), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) { - return $_valid_public_ip; // A valid public IPv4 or IPv6 address. - } - } - unset($_key, $_possible_ip, $_valid_public_ip); // Housekeeping. - - return ''; // Default return value. -}; diff --git a/src/includes/closures/Shared/PatternUtils.php b/src/includes/closures/Shared/PatternUtils.php deleted file mode 100644 index f82399d..0000000 --- a/src/includes/closures/Shared/PatternUtils.php +++ /dev/null @@ -1,48 +0,0 @@ -lineDelimitedPatternsToRegex = function ($patterns) use ($self) { - $regex = ''; // Initialize list of regex patterns. - $patterns = (string) $patterns; - - if (($patterns = preg_split('/['."\r\n".']+/', $patterns, -1, PREG_SPLIT_NO_EMPTY))) { - $regex = '/(?:'.implode('|', array_map($self->wdRegexToActualRegexFrag, $patterns)).')/i'; - } - return $regex; -}; - -/* - * Convert watered-down regex to actual regex. - * - * @since 151114 Enhancing exclusion pattern support. - * - * @param string $string Input watered-down regex to convert. - * - * @return string Actual regex pattern after conversion. - */ -$self->wdRegexToActualRegexFrag = function ($string) use ($self) { - return preg_replace( - array( - '/\\\\\^/', - '/\\\\\*\\\\\*/', - '/\\\\\*/', - '/\\\\\$/', - ), - array( - '^', // Beginning of line. - '.*?', // Zero or more chars. - '[^\/]*?', // Zero or more chars != /. - '$', // End of line. - ), - preg_quote((string) $string, '/') - ); -}; diff --git a/src/includes/closures/Shared/ReplaceUtils.php b/src/includes/closures/Shared/ReplaceUtils.php deleted file mode 100644 index a532ff9..0000000 --- a/src/includes/closures/Shared/ReplaceUtils.php +++ /dev/null @@ -1,43 +0,0 @@ -strReplaceOnce = function ($needle, $replace, $haystack, $caSe_insensitive = false) use ($self) { - $needle = (string) $needle; - $replace = (string) $replace; - $haystack = (string) $haystack; - $caSe_strpos = $caSe_insensitive ? 'stripos' : 'strpos'; - - if (($needle_strpos = $caSe_strpos($haystack, $needle)) === false) { - return $haystack; // Nothing to replace. - } - return (string) substr_replace($haystack, $replace, $needle_strpos, strlen($needle)); -}; - -/* - * String replace ONE time (caSe-insensitive). - * - * @since 150422 Rewrite. - * - * @param string $needle A string to search/replace. - * @param string $replace What to replace `$needle` with. - * @param string $haystack The string/haystack to search in. - * - * @return string The `$haystack`, with `$needle` replaced with `$replace` ONE time only. - */ -$self->strIreplaceOnce = function ($needle, $replace, $haystack) use ($self) { - return $self->strReplaceOnce($needle, $replace, $haystack, true); -}; diff --git a/src/includes/closures/Shared/ServerUtils.php b/src/includes/closures/Shared/ServerUtils.php deleted file mode 100644 index 3e85537..0000000 --- a/src/includes/closures/Shared/ServerUtils.php +++ /dev/null @@ -1,65 +0,0 @@ -isApache = function () use ($self) { - if (!is_null($is = &$self->staticKey('isApache'))) { - return $is; // Already cached this. - } - if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { - if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) { - return ($is = true); - } - if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Is running on Nginx? - * - * @since 151002 This is Nginx? - * - * @return bool True if running Nginx. - */ -$self->isNginx = function () use ($self) { - if (!is_null($is = &$self->staticKey('isNginx'))) { - return $is; // Already cached this. - } - if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { - if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Is running on Windows IIS? - * - * @since 151002 This is Windows IIS? - * - * @return bool True if running Windows IIS. - */ -$self->isIis = function () use ($self) { - if (!is_null($is = &$self->staticKey('isIis'))) { - return $is; // Already cached this. - } - if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { - if (stripos($_SERVER['SERVER_SOFTWARE'], 'microsoft-iis') !== false) { - return ($is = true); - } - if (stripos($_SERVER['SERVER_SOFTWARE'], 'expressiondevserver') !== false) { - return ($is = true); - } - } - return ($is = false); -}; diff --git a/src/includes/closures/Shared/StringUtils.php b/src/includes/closures/Shared/StringUtils.php deleted file mode 100644 index 9883c9d..0000000 --- a/src/includes/closures/Shared/StringUtils.php +++ /dev/null @@ -1,87 +0,0 @@ -clip = function ($value, $max_length = 80, $force_ellipsis = false) use ($self) { - if (is_array($value) || is_object($value)) { - foreach ($value as $_key => &$_value) { - $_value = $self->clip($_value, $max_length, $force_ellipsis); - } - unset($_key, $_value); // Housekeeping. - - return $value; - } - if (!($string = (string) $value)) { - return $string; // Empty. - } - $max_length = max(4, $max_length); - - $string = strip_tags($string); - $string = preg_replace('/\s+/', ' ', strip_tags($string)); - $string = trim($string); // Trim it up now. - - if (strlen($string) > $max_length) { - $string = (string) substr($string, 0, $max_length - 3).'...'; - } elseif ($force_ellipsis && strlen($string) + 3 > $max_length) { - $string = (string) substr($string, 0, $max_length - 3).'...'; - } else { - $string .= $force_ellipsis ? '...' : ''; - } - return $string; -}; - -/* - * Mid-clips string(s) to X chars deeply. - * - * @since 151114 Adding string utils. - * - * @param mixed $value Any input value. - * @param int $max_length Defaults to a value of `80`. - * - * @return string|array|object Mid-clipped value. - */ -$self->midClip = function ($value, $max_length = 80) use ($self) { - if (is_array($value) || is_object($value)) { - foreach ($value as $_key => &$_value) { - $_value = $self->midClip($_value, $max_length); - } - unset($_key, $_value); // Housekeeping. - - return $value; - } - if (!($string = (string) $value)) { - return $string; // Empty. - } - $max_length = max(4, $max_length); - - $string = strip_tags($string); - $string = preg_replace('/\s+/', ' ', strip_tags($string)); - $string = trim($string); // Trim it up now. - - if (strlen($string) <= $max_length) { - return $string; // Nothing to do. - } - $full_string = $string; - $half_max_length = floor($max_length / 2); - - $first_clip = $half_max_length - 3; - $string = $first_clip >= 1 // Something? - ? substr($full_string, 0, $first_clip).'...' - : '...'; // Ellipsis only. - - $second_clip = strlen($full_string) - ($max_length - strlen($string)); - $string .= $second_clip >= 0 && $second_clip >= $first_clip - ? substr($full_string, $second_clip) : ''; - - return $string; -}; diff --git a/src/includes/closures/Shared/SysUtils.php b/src/includes/closures/Shared/SysUtils.php deleted file mode 100644 index c935114..0000000 --- a/src/includes/closures/Shared/SysUtils.php +++ /dev/null @@ -1,92 +0,0 @@ -sysLoadAverages = function () use ($self) { - if (!is_null($averages = &$self->cacheKey('sysLoadAverages'))) { - return $averages; // Already cached these. - } - if (!$self->functionIsPossible('sys_getloadavg')) { - return ($averages = array()); - } - if (!is_array($averages = sys_getloadavg()) || !$averages) { - return ($averages = array()); - } - $averages = array_map('floatval', $averages); - $averages = array_slice($averages, 0, 3); - // i.e., 1m, 5m, 15m; see: - - return $averages; -}; - -/* - * System memory info. - * - * @since 151002 Adding cache directory statistics. - * - * @return \stdClass|boolean System memory info. - */ -$self->sysMemoryStatus = function () use ($self) { - if (!is_null($status = &$self->cacheKey('sysMemoryStatus'))) { - return $status; // Already cached this. - } - if (!$self->functionIsPossible('shell_exec')) { - return ($status = false); - } - if (!($free = trim((string) @shell_exec('free')))) { - return ($status = false); - } - if (!($free_lines = explode("\n", $free))) { - return ($status = false); - } - if (empty($free_lines[1])) { - return ($status = false); - } - if (!($memory = explode(' ', $free_lines[1]))) { - return ($status = false); - } - if (!($memory = array_merge(array_filter($memory)))) { - return ($status = false); - } - if (!isset($memory[1], $memory[2])) { - return ($status = false); - } - if (($total = (integer) $memory[1]) <= 0) { - return ($status = false); - } - $used = (integer) $memory[2]; - $percent = $used / $total * 100; - $percentage = sprintf(__('%s%%', 'comet-cache'), number_format($percent, 2, '.', '')); - $status = (object) compact('total', 'used', 'percent', 'percentage'); - - return $status; -}; - -/* - * System opcache status/details. - * - * @since 151002 Adding cache directory statistics. - * - * @return \stdClass|boolean System opcache status/details. - */ -$self->sysOpcacheStatus = function () use ($self) { - if (!is_null($status = &$self->cacheKey('sysOpcacheStatus'))) { - return $status; // Already cached this. - } - if (!$self->functionIsPossible('opcache_get_status')) { - return ($status = false); - } - if (!is_array($status = opcache_get_status(false)) || !$status) { - return ($status = false); - } - if (empty($status['opcache_enabled'])) { - return ($status = false); - } - return json_decode(json_encode($status)); -}; diff --git a/src/includes/closures/Shared/TokenUtils.php b/src/includes/closures/Shared/TokenUtils.php deleted file mode 100644 index cc42f90..0000000 --- a/src/includes/closures/Shared/TokenUtils.php +++ /dev/null @@ -1,300 +0,0 @@ -hostToken = function ($dashify = false, $consider_domain_mapping = false, $consider_domain_mapping_domain = '') use ($self) { - if (!is_null($token = &$self->staticKey('hostToken', array($dashify, $consider_domain_mapping, $consider_domain_mapping_domain)))) { - return $token; // Already cached this. - } - $token = ''; // Initialize token value. - - if (!is_multisite() || $self->isAdvancedCache()) { - $token = (string) $_SERVER['HTTP_HOST']; - } elseif ($consider_domain_mapping && $self->canConsiderDomainMapping()) { - if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) { - $token = $consider_domain_mapping_domain; - } elseif ($self->isDomainMapping()) { - $token = (string) $_SERVER['HTTP_HOST']; - } else { // For the current blog ID. - $token = $self->domainMappingUrlFilter($self->currentUrl()); - $token = $self->parseUrl($token, PHP_URL_HOST); - } - } - if (!$token) { // Use default? - $token = (string) $_SERVER['HTTP_HOST']; - } - if ($token) { // Have token? - $token = strtolower($token); - if ($dashify) { // Dashify it? - $token = preg_replace('/[^a-z0-9]/i', '-', $token); - $token = trim($token, '-'); - } - } - return $token; -}; - -/* - * Host for a specific blog. - * - * @since 150821 Improving multisite compat. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @param string $consider_domain_mapping_domain A specific domain? - * - * @param boolean $fallback Fallback on blog's domain when mapping? - * - * @param integer $blog_id For which blog ID? - * - * @return string Host for a specific blog. - * - * @note The return value of this function is NOT cached in support of `switch_to_blog()`. - */ -$self->hostTokenForBlog = function ($dashify = false, $consider_domain_mapping = false, $consider_domain_mapping_domain = '', $fallback = false, $blog_id = 0) use ($self) { - if (!is_multisite() || $self->isAdvancedCache()) { - return $self->hostToken($dashify, $consider_domain_mapping, $consider_domain_mapping_domain); - } - $token = ''; // Initialize token value. - - if ($consider_domain_mapping && $self->canConsiderDomainMapping()) { - if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) { - $token = $consider_domain_mapping_domain; // Force this value. - } else { - $token = $self->domainMappingBlogDomain($blog_id, $fallback); - } - } elseif (($blog_details = $self->blogDetails($blog_id))) { - $token = $blog_details->domain; // Unmapped domain. - } - if ($token) { // Have token? - $token = strtolower($token); - if ($dashify) { // Dashify it? - $token = preg_replace('/[^a-z0-9]/i', '-', $token); - $token = trim($token, '-'); - } - } - return $token; -}; - -/* - * Current site's base directory. - * - * @since 150422 Rewrite. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @return string Current site's base directory. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->hostBaseToken = function ($dashify = false, $consider_domain_mapping = false) use ($self) { - if (!is_null($token = &$self->staticKey('hostBaseToken', array($dashify, $consider_domain_mapping)))) { - return $token; // Already cached this. - } - $token = '/'; // Assume NOT multisite; or own domain. - - if (!is_multisite()) { - return $token; // Not applicable. - } - if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { - return $token; // Not applicable. - } - if ($consider_domain_mapping && $self->canConsiderDomainMapping()) { - return $token; // Not applicable. - } - if (defined('PATH_CURRENT_SITE')) { - $token = (string) PATH_CURRENT_SITE; - } - $token = trim($token, '\\/'." \t\n\r\0\x0B"); - $token = isset($token[0]) ? '/'.$token.'/' : '/'; - - if ($token !== '/' && $dashify) { - $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); - $token = trim($token, '-'); - } - return $token; -}; - -/* - * Current blog's sub-directory. - * - * @since 150422 Rewrite. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @param string $path Defaults to the current URI path. - * - * @return string Current blog's sub-directory. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->hostDirToken = function ($dashify = false, $consider_domain_mapping = false, $path = null) use ($self) { - if (!isset($path)) { // Use current/default path? - $path = (string) $self->parseUrl($_SERVER['REQUEST_URI'], PHP_URL_PATH); - } - $path = '/'.ltrim((string) $path, '/'); // Force leading slash. - - if (!is_null($token = &$self->staticKey('hostDirToken', array($dashify, $consider_domain_mapping, $path)))) { - return $token; // Already cached this. - } - $token = '/'; // Assume NOT multisite; or own domain. - - if (!is_multisite()) { - return $token; // Not applicable. - } - if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { - return $token; // Not applicable. - } - if ($consider_domain_mapping && $self->canConsiderDomainMapping()) { - return $token; // Not applicable. - } - if ($path && $path !== '/' && ($host_base_token = trim($self->hostBaseToken(), '/'))) { - $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path); - } else { - $path_minus_base = $path; // Default value. - } - list($token) = explode('/', trim($path_minus_base, '/')); - $token = trim($token, '\\/'." \t\n\r\0\x0B"); - $token = isset($token[0]) ? '/'.$token.'/' : '/'; - - if ($token !== '/') { // Perhaps NOT the main site? - $blog_paths_file = $self->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths'; - if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) { - $token = '/'; // NOT a real/valid child blog path. - } - } - if ($token !== '/' && $dashify) { - $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); - $token = trim($token, '-'); - } - return $token; -}; - -/* - * A blog's sub-directory. - * - * @since 150821 Improving multisite compat. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @param integer $blog_id For which blog ID? - * - * @return string A blog's sub-directory. - * - * @note The return value of this function is NOT cached in support of `switch_to_blog()`. - */ -$self->hostDirTokenForBlog = function ($dashify = false, $consider_domain_mapping = false, $blog_id = 0) use ($self) { - if (!is_multisite() || $self->isAdvancedCache()) { - return $self->hostDirToken($dashify, $consider_domain_mapping); - } - $token = '/'; // Initialize token value. - - if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { - return $token; // Not applicable. - } - if ($consider_domain_mapping && $self->canConsiderDomainMapping()) { - return $token; // Not applicable. - } - if (($blog_details = $self->blogDetails($blog_id))) { - $path = $blog_details->path; // e.g., `[/base]/path/` (includes base). - if ($path && $path !== '/' && ($host_base_token = trim($self->hostBaseToken(), '/'))) { - $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path); - } else { - $path_minus_base = $path; // Default value. - } - list($token) = explode('/', trim($path_minus_base, '/')); - } - $token = trim($token, '\\/'." \t\n\r\0\x0B"); - $token = isset($token[0]) ? '/'.$token.'/' : '/'; - - if ($token !== '/') { // Perhaps NOT the main site? - $blog_paths_file = $self->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths'; - if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) { - $token = '/'; // NOT a real/valid child blog path. - } - } - if ($token !== '/' && $dashify) { - $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); - $token = trim($token, '-'); - } - return $token; -}; - -/* - * Current site's base directory & current blog's sub-directory. - * - * @since 150422 Rewrite. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @param string $path Defaults to the current URI path. - * - * @return string Current site's base directory & current blog's sub-directory. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->hostBaseDirTokens = function ($dashify = false, $consider_domain_mapping = false, $path = null) use ($self) { - if (!is_null($tokens = &$self->staticKey('hostBaseDirTokens', array($dashify, $consider_domain_mapping, $path)))) { - return $tokens; // Already cached this. - } - $tokens = $self->hostBaseToken($dashify, $consider_domain_mapping); - $tokens .= $self->hostDirToken($dashify, $consider_domain_mapping, $path); - - return ($tokens = preg_replace('/\/+/', '/', $tokens)); -}; - -/* - * A site's base directory & a blog's sub-directory. - * - * @since 150821 Improving multisite compat. - * - * @param boolean $dashify Optional, defaults to a `FALSE` value. - * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`. - * - * @param boolean $consider_domain_mapping Consider? - * - * @param integer $blog_id For which blog ID? - * - * @return string A site's base directory & a blog's sub-directory. - * - * @note The return value of this function is NOT cached in support of `switch_to_blog()`. - */ -$self->hostBaseDirTokensForBlog = function ($dashify = false, $consider_domain_mapping = false, $blog_id = 0) use ($self) { - $tokens = $self->hostBaseToken($dashify, $consider_domain_mapping); - $tokens .= $self->hostDirTokenForBlog($dashify, $consider_domain_mapping, $blog_id); - - return ($tokens = preg_replace('/\/+/', '/', $tokens)); -}; - - diff --git a/src/includes/closures/Shared/TrimUtils.php b/src/includes/closures/Shared/TrimUtils.php deleted file mode 100644 index fc5b260..0000000 --- a/src/includes/closures/Shared/TrimUtils.php +++ /dev/null @@ -1,36 +0,0 @@ -trimDeep = function ($values, $chars = '', $extra_chars = '') use ($self) { - if (is_array($values) || is_object($values)) { - foreach ($values as $_key => &$_values) { - $_values = $self->trimDeep($_values, $chars, $extra_chars); - } - unset($_key, $_values); // Housekeeping. - - return $values; - } - $string = (string) $values; - $chars = (string) $chars; - $extra_chars = (string) $extra_chars; - - $chars = isset($chars[0]) ? $chars : " \r\n\t\0\x0B"; - $chars = $chars.$extra_chars; // Concatenate. - - return trim($string, $chars); -}; diff --git a/src/includes/closures/Shared/UrlUtils.php b/src/includes/closures/Shared/UrlUtils.php deleted file mode 100644 index 587706f..0000000 --- a/src/includes/closures/Shared/UrlUtils.php +++ /dev/null @@ -1,131 +0,0 @@ -parseUrl = function ($url_uri_qsl, $component = -1) use ($self) { - $url_uri_qsl = (string) $url_uri_qsl; - $component = (integer) $component; - ${'//'} = strpos($url_uri_qsl, '//') === 0; - - if ($url_uri_qsl && strpos($url_uri_qsl, '&') !== false) { - $url_uri_qsl = str_replace('&', '&', $url_uri_qsl); - } - if ($component > -1) { - if (${'//'} && $component === PHP_URL_SCHEME) { - return ($part = '//'); - } - return ($part = parse_url($url_uri_qsl, $component)); - } else { - if (!is_array($parts = parse_url($url_uri_qsl))) { - return ($parts = array()); - } - if (${'//'}) { - $parts['scheme'] = '//'; - } - return $parts; - } -}; - -/* - * Unparses a URL. - * - * @since 150821 Improving multisite compat. - * - * @param array $parts Input URL parts. - * - * @return string Unparsed URL in string format. - */ -$self->unParseUrl = function (array $parts) use ($self) { - $scheme = ''; - $host = ''; - $port = ''; - $user = ''; - $pass = ''; - $path = ''; - $query = ''; - $fragment = ''; - - if (!empty($parts['scheme'])) { - if ($parts['scheme'] === '//') { - $scheme = $parts['scheme']; - } else { - $scheme = $parts['scheme'].'://'; - } - } - if (!empty($parts['host'])) { - $host = $parts['host']; - } - if (!empty($parts['port'])) { - $port = ':'.$parts['port']; - } - if (!empty($parts['user'])) { - $user = $parts['user']; - } - if (!empty($parts['pass'])) { - $pass = $parts['pass']; - } - if ($user || $pass) { - $pass .= '@'; - } - if (!empty($parts['path'])) { - $path = '/'.ltrim($parts['path'], '/'); - } - if (!empty($parts['query'])) { - $query = '?'.$parts['query']; - } - if (!empty($parts['fragment'])) { - $fragment = '#'.$parts['fragment']; - } - return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; -}; - -/* - * Is the current request over SSL? - * - * @since 150422 Rewrite. - * - * @return boolean `TRUE` if the current request is over SSL. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. - */ -$self->isSsl = function () use ($self) { - if (!is_null($is = &$self->staticKey('isSsl'))) { - return $is; // Already cached this. - } - if (!empty($_SERVER['SERVER_PORT'])) { - if ((integer) $_SERVER['SERVER_PORT'] === 443) { - return ($is = true); - } - } - if (!empty($_SERVER['HTTPS'])) { - if (filter_var($_SERVER['HTTPS'], FILTER_VALIDATE_BOOLEAN)) { - return ($is = true); - } - } - if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { - if (strcasecmp((string) $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0) { - return ($is = true); - } - } - return ($is = false); -}; - -/* - * Current URL. - * - * @since 150821 Improving multisite compat. - * - * @return string Current URL. - */ -$self->currentUrl = function () use ($self) { - return ($self->isSsl() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; -}; diff --git a/src/includes/functions/i18n-utils.php b/src/includes/functions/i18n-utils.php index e22314c..f954047 100644 --- a/src/includes/functions/i18n-utils.php +++ b/src/includes/functions/i18n-utils.php @@ -1,22 +1,37 @@ maybeSetDebugInfoPostload(); } if (!empty($advanced_cache->postload['wp_main_query'])) { - add_action('wp', array($advanced_cache, 'wpMainQueryPostload'), PHP_INT_MAX); + add_action('wp', [$advanced_cache, 'wpMainQueryPostload'], PHP_INT_MAX); } $advanced_cache->doWpAction('after_'.$GLOBAL_NS.'_'.__FUNCTION__, get_defined_vars()); $advanced_cache->doWpAction($GLOBAL_NS.'_'.__FUNCTION__.'_complete', get_defined_vars()); diff --git a/src/includes/interfaces/Shared/CachePathConsts.php b/src/includes/interfaces/Shared/CachePathConsts.php new file mode 100644 index 0000000..1d39db4 --- /dev/null +++ b/src/includes/interfaces/Shared/CachePathConsts.php @@ -0,0 +1,140 @@ +sendNoCacheHeaders(); // Disallow. + } // Else, allow client-side caching; because `ABC` is a true-ish value. + // ↑ Note that exclusion patterns are ignored in this case, in favor of `ABC`. + } elseif (COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS && preg_match(COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS, $_SERVER['REQUEST_URI'])) { + return $this->sendNoCacheHeaders(); // Disallow. + } + return; // Allow browser caching; default behavior in this mode. + + case false: // Global config disallows; check inclusions. + + if (isset($_GET[strtolower(SHORT_NAME).'ABC'])) { + if (filter_var($_GET[strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) { + return; // Allow, because `ABC` is a false-ish value. + } // Else, disallow client-side caching; because `ABC` is a true-ish value. + // ↑ Note that inclusion patterns are ignored in this case, in favor of `ABC`. + } + return $this->sendNoCacheHeaders(); // Disallow; default behavior in this mode. + } + } +} diff --git a/src/includes/traits/Ac/NcDebugUtils.php b/src/includes/traits/Ac/NcDebugUtils.php new file mode 100644 index 0000000..395bf05 --- /dev/null +++ b/src/includes/traits/Ac/NcDebugUtils.php @@ -0,0 +1,217 @@ + '', 'reason' => '']; + + /** + * Used to setup debug info (if enabled). + * + * @since 150422 Rewrite. + * + * @param string $reason_code One of the `NC_DEBUG_` constants. + * @param string $reason Optionally override the built-in description with a custom message. + */ + public function maybeSetDebugInfo($reason_code, $reason = '') + { + if (!COMET_CACHE_DEBUGGING_ENABLE) { + return; // Nothing to do. + } + $reason = (string) $reason; + if (!($reason_code = (string) $reason_code)) { + return; // Not applicable. + } + $this->debug_info = ['reason_code' => $reason_code, 'reason' => $reason]; + } + + /** + * Echoes `NC_DEBUG_` info in the WordPress `shutdown` phase (if applicable). + * + * @since 150422 Rewrite. + * + * @attaches-to `shutdown` hook in WordPress w/ a late priority. + */ + public function maybeEchoNcDebugInfo() + { + if (!COMET_CACHE_DEBUGGING_ENABLE) { + return; // Nothing to do. + } + if (is_admin()) { + return; // Not applicable. + } + if (strcasecmp(PHP_SAPI, 'cli') === 0) { + return; // Let's not run the risk here. + } + if ($this->debug_info && $this->hasACacheableContentType() && $this->is_a_wp_content_type) { + echo (string) $this->maybeGetNcDebugInfo($this->debug_info['reason_code'], $this->debug_info['reason']); + } + } + + /** + * Gets `NC_DEBUG_` info (if applicable). + * + * @since 150422 Rewrite. + * + * @param string $reason_code One of the `NC_DEBUG_` constants. + * @param string $reason Optional; to override the default description with a custom message. + * + * @return string The debug info; i.e. full description (if applicable). + */ + public function maybeGetNcDebugInfo($reason_code = '', $reason = '') + { + if (!COMET_CACHE_DEBUGGING_ENABLE) { + return ''; // Not applicable. + } + $reason = (string) $reason; + if (!($reason_code = (string) $reason_code)) { + return ''; // Not applicable. + } + if (!$reason) { + switch ($reason_code) { + case $this::NC_DEBUG_PHP_SAPI_CLI: + $reason = __('because `PHP_SAPI` reports that you are currently running from the command line.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_NO_SERVER_HTTP_HOST: + $reason = __('because `$_SERVER[\'HTTP_HOST\']` is missing from your server configuration.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_NO_SERVER_REQUEST_URI: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` is missing from your server configuration.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT: + if ($this->functionIsPossible('did_action') && did_action('ws_plugin__s2member_during_no_cache_constants')) { + $reason = __('because the s2Member plugin set the PHP constant `COMET_CACHE_ALLOWED` to a boolean-ish `FALSE` value at runtime. The s2Member plugin is serving content that must remain dynamic on this particular page, and therefore this page was intentionally not cached for a very good reason.', 'comet-cache'); + } else { + $reason = __('because the PHP constant `COMET_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); + } + break; // Break switch handler. + + case $this::NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR: + $reason = __('because the environment variable `$_SERVER[\'COMET_CACHE_ALLOWED\']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT: + $reason = __('because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR: + $reason = __('because the environment variable `$_SERVER[\'DONOTCACHEPAGE\']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it\'s usually for a very good reason.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_AC_GET_VAR: + $reason = sprintf(__('because `$_GET[\'%1$sAC\']` is set to a boolean-ish FALSE value.', 'comet-cache'), strtolower(SHORT_NAME)); + break; // Break switch handler. + + case $this::NC_DEBUG_UNCACHEABLE_REQUEST: + $reason = __('because `$_SERVER[\'REQUEST_METHOD\']` is `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`. These request methods should never (ever) be cached in any way.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_SELF_SERVE_REQUEST: + $reason = __('because `[current IP address]` === `$_SERVER[\'SERVER_ADDR\']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define(\'LOCALHOST\', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_FEED_REQUEST: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_WP_SYSTEMATICS: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_WP_ADMIN: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` or the `is_admin()` function indicates this is an administrative area of the site.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_MS_FILES: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_IS_LOGGED_IN_USER: + case $this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER: + $reason = __('because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you\'ve left (or replied to) a comment recently. If this message continues, please clear your cookies and try again.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_IS_LOGGED_IN_USER_NONCE: + $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. While your current configuration states that pages SHOULD be cache for logged-in visitors, `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_PAGE_CONTAINS_NONCE: + $reason = __('because the current page contains `_wpnonce` or `akismet_comment_nonce`. Note that `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_NO_USER_TOKEN: + $reason = sprintf(__('because the current user appeared to be logged into the site (in one way or another); but %1$s was unable to formulate a User Token for them. Please report this as a possible bug.', 'comet-cache'), NAME); + break; // Break switch handler. + + case $this::NC_DEBUG_GET_REQUEST_QUERIES: + $reason = __('because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_PREVIEW: + $reason = __('because `$_REQUEST` indicates this is simply a preview of something to come.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_EXCLUDED_URIS: + $reason = __('because `$_SERVER[\'REQUEST_URI\']` matches a configured URI Exclusion Pattern on this installation.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_EXCLUDED_AGENTS: + $reason = __('because `$_SERVER[\'HTTP_USER_AGENT\']` matches a configured User-Agent Exclusion Pattern on this installation.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_EXCLUDED_REFS: + $reason = __('because `$_SERVER[\'HTTP_REFERER\']` and/or `$_GET[\'_wp_http_referer\']` matches a configured HTTP Referrer Exclusion Pattern on this installation.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_404_REQUEST: + $reason = __('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_MAINTENANCE_PLUGIN: + $reason = __('because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_OB_ZLIB_CODING_TYPE: + $reason = sprintf(__('because %1$s is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. %1$s is NOT compatible with `ob_gzhandler()` and others like this.', 'comet-cache'), NAME); + break; // Break switch handler. + + case $this::NC_DEBUG_WP_ERROR_PAGE: + $reason = __('because the contents of this document contain ``, which indicates this is an auto-generated WordPress error message.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_UNCACHEABLE_CONTENT_TYPE: + $reason = __('because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_UNCACHEABLE_STATUS: + $reason = __('because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins.', 'comet-cache'); + break; // Break switch handler. + + case $this::NC_DEBUG_1ST_TIME_404_SYMLINK: + $reason = sprintf(__('because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it\'s happened on this page. Your current configuration says that 404 errors SHOULD be cached, so %1$s built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don\'t clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request.', 'comet-cache'), NAME); + break; // Break switch handler. + + case $this::NC_DEBUG_EARLY_BUFFER_TERMINATION: + $reason = sprintf(__('because %1$s detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It\'s not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since %1$s is effectively disabled at runtime when this occurs.', 'comet-cache'), NAME); + break; // Break switch handler. + + default: // Default case handler. + $reason = __('due to an unexpected behavior in the application. Please report this as a bug!', 'comet-cache'); + break; // Break switch handler. + } + } + return "\n".''; + } +} diff --git a/src/includes/traits/Ac/ObUtils.php b/src/includes/traits/Ac/ObUtils.php new file mode 100644 index 0000000..368da37 --- /dev/null +++ b/src/includes/traits/Ac/ObUtils.php @@ -0,0 +1,356 @@ +maybeSetDebugInfo($this::NC_DEBUG_PHP_SAPI_CLI); + } + if (empty($_SERVER['HTTP_HOST']) || !$this->hostToken()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_NO_SERVER_HTTP_HOST); + } + if (empty($_SERVER['REQUEST_URI'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_NO_SERVER_REQUEST_URI); + } + if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT); + } + if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR); + } + if (defined('DONOTCACHEPAGE')) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT); + } + if (isset($_SERVER['DONOTCACHEPAGE'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); + } + if (isset($_GET[strtolower(SHORT_NAME).'AC']) && !filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN)) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_AC_GET_VAR); + } + if ($this->isUncacheableRequestMethod()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_UNCACHEABLE_REQUEST); + } + if (isset($_SERVER['SERVER_ADDR']) && $this->currentIp() === $_SERVER['SERVER_ADDR']) { + if ((!IS_PRO || !$this->isAutoCacheEngine()) && !$this->isLocalhost()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_SELF_SERVE_REQUEST); + } + } + if (!COMET_CACHE_FEEDS_ENABLE && $this->isFeed()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_FEED_REQUEST); + } + if (preg_match('/\/(?:wp\-[^\/]+|xmlrpc)\.php(?:[?]|$)/i', $_SERVER['REQUEST_URI'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_WP_SYSTEMATICS); + } + if (is_admin() || preg_match('/\/wp-admin(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_WP_ADMIN); + } + if (is_multisite() && preg_match('/\/files(?:[\/?]|$)/i', $_SERVER['REQUEST_URI'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_MS_FILES); + } + if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $this->isLikeUserLoggedIn()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER); + } + if (!COMET_CACHE_GET_REQUESTS && $this->requestContainsUncacheableQueryVars()) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_GET_REQUEST_QUERIES); + } + if (!empty($_REQUEST['preview'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_PREVIEW); + } + if (COMET_CACHE_EXCLUDE_URIS && preg_match(COMET_CACHE_EXCLUDE_URIS, $_SERVER['REQUEST_URI'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_URIS); + } + if (COMET_CACHE_EXCLUDE_AGENTS && !empty($_SERVER['HTTP_USER_AGENT']) && (!IS_PRO || !$this->isAutoCacheEngine())) { + if (preg_match(COMET_CACHE_EXCLUDE_AGENTS, $_SERVER['HTTP_USER_AGENT'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_AGENTS); + } + } + if (COMET_CACHE_EXCLUDE_REFS && !empty($_REQUEST['_wp_http_referer'])) { + if (preg_match(COMET_CACHE_EXCLUDE_REFS, stripslashes($_REQUEST['_wp_http_referer']))) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_REFS); + } + } + if (COMET_CACHE_EXCLUDE_REFS && !empty($_SERVER['HTTP_REFERER'])) { + if (preg_match(COMET_CACHE_EXCLUDE_REFS, $_SERVER['HTTP_REFERER'])) { + return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_REFS); + } + } + $this->protocol = $this->isSsl() ? 'https://' : 'http://'; + $this->host_token = $this->hostToken(); + $this->host_base_dir_tokens = $this->hostBaseDirTokens(); + + $this->version_salt = ''; // Initialize the version salt. + + $this->version_salt = $this->applyFilters(get_class($this).'__version_salt', $this->version_salt); + $this->version_salt = $this->applyFilters(GLOBAL_NS.'_version_salt', $this->version_salt); + + $this->cache_path = $this->buildCachePath($this->protocol.$this->host_token.$_SERVER['REQUEST_URI'], '', $this->version_salt); + $this->cache_file = COMET_CACHE_DIR.'/'.$this->cache_path; // Not considering a user cache. That's done in the postload phase. + + $this->cache_path_404 = $this->buildCachePath($this->protocol.$this->host_token.rtrim($this->host_base_dir_tokens, '/').'/'.COMET_CACHE_404_CACHE_FILENAME); + $this->cache_file_404 = COMET_CACHE_DIR.'/'.$this->cache_path_404; // Not considering a user cache at all here--ever. + + $this->salt_location = ltrim($this->version_salt.' '.$this->protocol.$this->host_token.$_SERVER['REQUEST_URI']); + + $this->cache_max_age = strtotime('-'.COMET_CACHE_MAX_AGE); + + if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN === 'postload' && $this->isLikeUserLoggedIn()) { + $this->postload['when_logged_in'] = true; // Enable postload check. + } elseif (is_file($this->cache_file) && (!$this->cache_max_age || filemtime($this->cache_file) >= $this->cache_max_age)) { + list($headers, $cache) = explode('', file_get_contents($this->cache_file), 2); + + $headers_list = $this->headersList(); + foreach (unserialize($headers) as $_header) { + if (!in_array($_header, $headers_list, true) && stripos($_header, 'Last-Modified:') !== 0) { + header($_header); // Only cacheable/safe headers are stored in the cache. + } + } + unset($_header); // Just a little housekeeping. + + if (COMET_CACHE_DEBUGGING_ENABLE && $this->isHtmlXmlDoc($cache)) { + $total_time = number_format(microtime(true) - $this->timer, 5, '.', ''); + $cache .= "\n".''; + // translators: This string is actually NOT translatable because the `__()` function is not available at this point in the processing. + $cache .= "\n".''; + } + exit($cache); // Exit with cache contents. + } else { + ob_start([$this, 'outputBufferCallbackHandler']); + } + return; // Return value not applicable. + } + + /** + * Output buffer handler; i.e. the cache file generator. + * + * @note We CANNOT depend on any WP functionality here; it will cause problems. + * Anything we need from WP should be saved in the postload phase as a scalar value. + * + * @since 150422 Rewrite. + * + * @param string $buffer The buffer from {@link \ob_start()}. + * @param int $phase A set of bitmask flags. + * + * @throws \Exception If unable to handle output buffering for any reason. + * + * @return string|bool The output buffer, or `FALSE` to indicate no change. + * + * @attaches-to {@link \ob_start()} + */ + public function outputBufferCallbackHandler($buffer, $phase) + { + if (!($phase & PHP_OUTPUT_HANDLER_END)) { + throw new \Exception(sprintf(__('Unexpected OB phase: `%1$s`.', 'comet-cache'), $phase)); + } + Classes\AdvCacheBackCompat::zenCacheConstants(); + + $cache = trim((string) $buffer); + + if (!isset($cache[0])) { + return false; // Don't cache an empty buffer. + } + if (!isset($GLOBALS[GLOBAL_NS.'_shutdown_flag'])) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_EARLY_BUFFER_TERMINATION); + } + if (defined('COMET_CACHE_ALLOWED') && !COMET_CACHE_ALLOWED) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_COMET_CACHE_ALLOWED_CONSTANT); + } + if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR); + } + if (defined('DONOTCACHEPAGE')) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT); + } + if (isset($_SERVER['DONOTCACHEPAGE'])) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_SERVER_VAR); + } + if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $this->is_user_logged_in) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_IS_LOGGED_IN_USER); + } + if ((!IS_PRO || !COMET_CACHE_WHEN_LOGGED_IN) && $this->isLikeUserLoggedIn()) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_IS_LIKE_LOGGED_IN_USER); + } + if (!COMET_CACHE_CACHE_NONCE_VALUES && preg_match('/\b(?:_wpnonce|akismet_comment_nonce)\b/', $cache)) { + if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $this->isLikeUserLoggedIn()) { + if (!COMET_CACHE_CACHE_NONCE_VALUES_WHEN_LOGGED_IN) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_IS_LOGGED_IN_USER_NONCE); + } + } else { // Use the default debug notice for nonce conflicts. + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_PAGE_CONTAINS_NONCE); + } // An nonce makes the page dynamic; i.e., NOT cache compatible. + } + if ($this->is_404 && !COMET_CACHE_CACHE_404_REQUESTS) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_404_REQUEST); + } + if (stripos($cache, '') !== false) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_WP_ERROR_PAGE); + } + if (!$this->hasACacheableContentType()) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_UNCACHEABLE_CONTENT_TYPE); + } + if (!$this->hasACacheableStatus()) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_UNCACHEABLE_STATUS); + } + if ($this->is_maintenance) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_MAINTENANCE_PLUGIN); + } + if ($this->functionIsPossible('zlib_get_coding_type') && zlib_get_coding_type() + && (!($zlib_oc = ini_get('zlib.output_compression')) || !filter_var($zlib_oc, FILTER_VALIDATE_BOOLEAN)) + ) { + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_OB_ZLIB_CODING_TYPE); + } + # Lock the cache directory while writes take place here. + + $cache_lock = $this->cacheLock(); // Lock cache directory. + + # Construct a temp file for atomic cache writes. + + $cache_file_tmp = $this->addTmpSuffix($this->cache_file); + + # Cache directory checks. The cache file directory is created here if necessary. + + if (!is_dir(COMET_CACHE_DIR) && mkdir(COMET_CACHE_DIR, 0775, true) && !is_file(COMET_CACHE_DIR.'/.htaccess')) { + file_put_contents(COMET_CACHE_DIR.'/.htaccess', $this->htaccess_deny); + } + if (!is_dir($cache_file_dir = dirname($this->cache_file))) { + $cache_file_dir_writable = mkdir($cache_file_dir, 0775, true); + } + if (empty($cache_file_dir_writable) && !is_writable($cache_file_dir)) { + throw new \Exception(sprintf(__('Cache directory not writable. %1$s needs this directory please: `%2$s`. Set permissions to `755` or higher; `777` might be needed in some cases.', 'comet-cache'), NAME, $cache_file_dir)); + } + # This is where a new 404 request might be detected for the first time. + + if ($this->is_404 && is_file($this->cache_file_404)) { + if (!(symlink($this->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $this->cache_file))) { + throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $this->cache_file, $this->cache_file_404, COMET_CACHE_DIR)); + } + $this->cacheUnlock($cache_lock); // Release. + return (boolean) $this->maybeSetDebugInfo($this::NC_DEBUG_1ST_TIME_404_SYMLINK); + } + /* ------- Otherwise, we need to construct & store a new cache file. ----------------------------------------------- */ + + + + if (COMET_CACHE_DEBUGGING_ENABLE && $this->isHtmlXmlDoc($cache)) { + $total_time = number_format(microtime(true) - $this->timer, 5, '.', ''); // Based on the original timer. + $cache .= "\n".''; + $cache .= "\n".''; + $cache .= "\n".''; + } + if ($this->is_404) { + if (file_put_contents($cache_file_tmp, serialize($this->cacheableHeadersList()).''.$cache) && rename($cache_file_tmp, $this->cache_file_404)) { + if (!(symlink($this->cache_file_404, $cache_file_tmp) && rename($cache_file_tmp, $this->cache_file))) { + throw new \Exception(sprintf(__('Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), $this->cache_file, $this->cache_file_404, COMET_CACHE_DIR)); + } + $this->cacheUnlock($cache_lock); // Release. + return $cache; // Return the newly built cache; with possible debug information also. + } + } elseif (file_put_contents($cache_file_tmp, serialize($this->cacheableHeadersList()).''.$cache) && rename($cache_file_tmp, $this->cache_file)) { + $this->cacheUnlock($cache_lock); // Release. + return $cache; // Return the newly built cache; with possible debug information also. + } + @unlink($cache_file_tmp); // Clean this up (if it exists); and throw an exception with information for the site owner. + throw new \Exception(sprintf(__('%1$s: failed to write cache file for: `%2$s`; possible permissions issue (or race condition), please check your cache directory: `%3$s`.', 'comet-cache'), NAME, $_SERVER['REQUEST_URI'], COMET_CACHE_DIR)); + } +} diff --git a/src/includes/traits/Ac/PostloadUtils.php b/src/includes/traits/Ac/PostloadUtils.php new file mode 100644 index 0000000..ce7f7a0 --- /dev/null +++ b/src/includes/traits/Ac/PostloadUtils.php @@ -0,0 +1,193 @@ + true, + 'wp_main_query' => true, + 'set_debug_info' => COMET_CACHE_DEBUGGING_ENABLE, + ]; + + + + + + + + + + /** + * Filters WP {@link \status_header()} (if applicable). + * + * @since 150422 Rewrite. + */ + public function maybeFilterStatusHeaderPostload() + { + if (empty($this->postload['filter_status_header'])) { + return; // Nothing to do in this case. + } + + add_filter( + 'status_header', + function ($status_header, $status_code) { + if ($status_code > 0) { + $this->http_status = (integer) $status_code; + } + return $status_header; + }, + PHP_INT_MAX, + 2 + ); + } + + /** + * Hooks `NC_DEBUG_` info into the WordPress `shutdown` phase (if applicable). + * + * @since 150422 Rewrite. + */ + public function maybeSetDebugInfoPostload() + { + if (!COMET_CACHE_DEBUGGING_ENABLE) { + return; // Nothing to do. + } + if (empty($this->postload['set_debug_info'])) { + return; // Nothing to do in this case. + } + if (is_admin()) { + return; // Not applicable. + } + if (strcasecmp(PHP_SAPI, 'cli') === 0) { + return; // Let's not run the risk here. + } + add_action('shutdown', [$this, 'maybeEchoNcDebugInfo'], PHP_INT_MAX - 10); + } + + /** + * Grab details from WP and the Comet Cache plugin itself, + * after the main query is loaded (if at all possible). + * + * This is where we have a chance to grab any values we need from WordPress; or from the CC plugin. + * It is EXTREMEMLY important that we NOT attempt to grab any object references here. + * Anything acquired in this phase should be stored as a scalar value. + * See {@link outputBufferCallbackHandler()} for further details. + * + * @since 150422 Rewrite. + * + * @attaches-to `wp` hook. + */ + public function wpMainQueryPostload() + { + if (empty($this->postload['wp_main_query'])) { + return; // Nothing to do in this case. + } + if ($this->is_wp_loaded_query || is_admin()) { + return; // Nothing to do. + } + if (!is_main_query()) { + return; // Not main query. + } + $this->is_wp_loaded_query = true; + $this->is_404 = is_404(); + $this->is_user_logged_in = is_user_logged_in(); + $this->content_url = rtrim(content_url(), '/'); + $this->is_maintenance = $this->functionIsPossible('is_maintenance') && is_maintenance(); + + add_action( + 'template_redirect', + function () { + $this->is_a_wp_content_type = $this->is_404 || $this->is_maintenance + || is_front_page() // See + || is_home() || is_singular() || is_archive() || is_post_type_archive() || is_tax() || is_search() || is_feed(); + }, + 11 + ); + } +} diff --git a/src/includes/traits/Ac/ShutdownUtils.php b/src/includes/traits/Ac/ShutdownUtils.php new file mode 100644 index 0000000..5f590ec --- /dev/null +++ b/src/includes/traits/Ac/ShutdownUtils.php @@ -0,0 +1,26 @@ +isBbPressActive()) { + return []; + } + if (!is_null($types = &$this->cacheKey('bbPressPostTypes'))) { + return $types; // Already did this. + } + $types = []; // Initialize. + $types[] = bbp_get_forum_post_type(); + $types[] = bbp_get_topic_post_type(); + $types[] = bbp_get_reply_post_type(); + + return $types; + } + + /** + * bbPress post statuses. + * + * @since 150821 Improving bbPress support. + * + * @return array All bbPress post statuses. + */ + public function bbPressStatuses() + { + if (!$this->isBbPressActive()) { + return []; + } + if (!is_null($statuses = &$this->cacheKey('bbPressStatuses'))) { + return $statuses; // Already did this. + } + $statuses = []; // Initialize. + + foreach (get_post_stati([], 'objects') as $_key => $_status) { + if (isset($_status->label_count['domain']) && $_status->label_count['domain'] === 'bbpress') { + $statuses[] = $_status->name; + } + } + unset($_key, $_status); // Housekeeping. + + return $statuses; + } +} diff --git a/src/includes/traits/Plugin/CleanupUtils.php b/src/includes/traits/Plugin/CleanupUtils.php new file mode 100644 index 0000000..e533591 --- /dev/null +++ b/src/includes/traits/Plugin/CleanupUtils.php @@ -0,0 +1,25 @@ +options['enable']) { + return; // Nothing to do. + } + + + + $this->wurgeCache(); // Purge now. + } +} diff --git a/src/includes/traits/Plugin/CondUtils.php b/src/includes/traits/Plugin/CondUtils.php new file mode 100644 index 0000000..0563372 --- /dev/null +++ b/src/includes/traits/Plugin/CondUtils.php @@ -0,0 +1,19 @@ + 900, + 'display' => __('Every 15 Minutes', 'comet-cache'), + ]; + return $schedules; + } + + /** + * Checks Cron setup, validates schedules, and reschedules events if necessary. + * + * @attaches-to `init` hook. + * + * @since 151220 Improving WP Cron setup and validation of schedules + */ + public function checkCronSetup() + { + if ($this->options['crons_setup'] < 1439005906 + || $this->options['crons_setup_on_namespace'] !== __NAMESPACE__ + || $this->options['crons_setup_with_cache_cleanup_schedule'] !== $this->options['cache_cleanup_schedule'] + || $this->options['crons_setup_on_wp_with_schedules'] !== sha1(serialize(wp_get_schedules())) + || !wp_next_scheduled('_cron_'.GLOBAL_NS.'_cleanup') + + ) { + wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); + wp_schedule_event(time() + 60, $this->options['cache_cleanup_schedule'], '_cron_'.GLOBAL_NS.'_cleanup'); + + + + $this->updateOptions( + [ + 'crons_setup' => time(), + 'crons_setup_on_namespace' => __NAMESPACE__, + 'crons_setup_with_cache_cleanup_schedule' => $this->options['cache_cleanup_schedule'], + 'crons_setup_on_wp_with_schedules' => sha1(serialize(wp_get_schedules())), + ] + ); + } + } + + /** + * Resets `crons_setup` and clears WP-Cron schedules. + * + * @since 151220 Fixing bug with Auto-Cache Engine cron disappearing in some scenarios + * + * @note This MUST happen upon uninstall and deactivation due to buggy WP_Cron behavior. Events with a custom schedule will disappear when plugin is not active (see http://bit.ly/1lGdr78). + */ + public function resetCronSetup() + { + if (is_multisite()) { // Main site CRON jobs. + switch_to_blog(get_current_site()->blog_id); + + wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); + restore_current_blog(); // Restore current blog. + } else { // Standard WP installation. + + wp_clear_scheduled_hook('_cron_'.GLOBAL_NS.'_cleanup'); + } + $this->updateOptions( + [ // Reset so that crons are rescheduled upon next activation + 'crons_setup' => $this->default_options['crons_setup'], + 'crons_setup_on_namespace' => $this->default_options['crons_setup_on_namespace'], + 'crons_setup_with_cache_cleanup_schedule' => $this->default_options['crons_setup_with_cache_cleanup_schedule'], + 'crons_setup_on_wp_with_schedules' => $this->default_options['crons_setup_on_wp_with_schedules'], + ] + ); + } +} diff --git a/src/includes/traits/Plugin/DbUtils.php b/src/includes/traits/Plugin/DbUtils.php new file mode 100644 index 0000000..de44768 --- /dev/null +++ b/src/includes/traits/Plugin/DbUtils.php @@ -0,0 +1,19 @@ +options['base_dir'])) { + throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache')); + } + $wp_content_base_dir_to = WP_CONTENT_DIR.'/'.$this->options['base_dir']; + + if (isset($rel_dir_file[0])) { + $wp_content_base_dir_to .= '/'.$rel_dir_file; + } + return $wp_content_base_dir_to; + } + + /** + * This constructs a relative/base directory path (no leading/trailing slashes). + * Always relative to {@link \WP_CONTENT_DIR}. Depends on the configured `base_dir` option value. + * + * @since 150422 Rewrite. + * + * @param string $rel_dir_file A sub-directory or file; relative location please. + * + * @throws \Exception If `base_dir` is empty when this method is called upon; + * i.e. if you attempt to call upon this method before {@link setup()} runs. + * + * @return string The relative/base directory path to `$rel_dir_file`. + */ + public function basePathTo($rel_dir_file) + { + $rel_dir_file = trim((string) $rel_dir_file, '\\/'." \t\n\r\0\x0B"); + + if (empty($this->options['base_dir'])) { + throw new \Exception(__('Missing `base_dir` option value.', 'comet-cache')); + } + $base_path_to = $this->options['base_dir']; + + if (isset($rel_dir_file[0])) { + $base_path_to .= '/'.$rel_dir_file; + } + return $base_path_to; + } + + /** + * Get the absolute filesystem path to the root of the WordPress installation. + * + * Copied verbatim from get_home_path() in wp-admin/includes/file.php + * + * @since 151114 Adding `.htaccess` tweaks. + * + * @return string Full filesystem path to the root of the WordPress installation + */ + public function wpHomePath() + { + $home = set_url_scheme(get_option('home'), 'http'); + $siteurl = set_url_scheme(get_option('siteurl'), 'http'); + if (!empty($home) && 0 !== strcasecmp($home, $siteurl)) { + $wp_path_rel_to_home = str_ireplace($home, '', $siteurl); /* $siteurl - $home */ + $pos = strripos(str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']), trailingslashit($wp_path_rel_to_home)); + $home_path = substr($_SERVER['SCRIPT_FILENAME'], 0, $pos); + $home_path = trailingslashit($home_path); + } else { + $home_path = ABSPATH; + } + return str_replace('\\', '/', $home_path); + } +} diff --git a/src/includes/traits/Plugin/HtaccessUtils.php b/src/includes/traits/Plugin/HtaccessUtils.php new file mode 100644 index 0000000..88ffa5a --- /dev/null +++ b/src/includes/traits/Plugin/HtaccessUtils.php @@ -0,0 +1,284 @@ +options['enable']) { + return true; // Nothing to do. + } + if (!$this->needHtaccessRules()) { + if ($this->findHtaccessMarker()) { // Do we need to clean up previously added rules? + $this->removeWpHtaccess(); // Fail silently since we don't need rules in place. + } + return true; // Nothing to do; no options enabled that require htaccess rules. + } + if (!$this->removeWpHtaccess()) { + return false; // Unable to remove. + } + if (!($htaccess = $this->readHtaccessFile())) { + return false; // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. + } + + $template_blocks = ''; // Initialize. + if (is_dir($templates_dir = dirname(dirname(__DIR__)).'/templates/htaccess')) { + foreach (scandir($templates_dir) as $_template_file) { + switch ($_template_file) { + + } + } + unset($_template_file); // Housekeeping. + } + + if (empty($template_blocks)) { // Do we need to add anything to htaccess? + $this->closeHtaccessFile($htaccess); // No need to write to htaccess file in this case. + return true; // Nothing to do, but no failures either. + } + + $template_header = '# BEGIN '.NAME.' '.$this->htaccess_marker.' (the '.$this->htaccess_marker.' marker is required for '.NAME.'; do not remove)'."\n"; + $template_footer = '# END '.NAME.' '.$this->htaccess_marker; + $htaccess['file_contents'] = $template_header.trim($template_blocks)."\n".$template_footer."\n\n".$htaccess['file_contents']; + + if (!$this->writeHtaccessFile($htaccess, true)) { + return false; // Failure; could not write changes. + } + + return true; // Added successfully. + } + + /** + * Remove template blocks from `/.htaccess` file. + * + * @since 151114 Adding `.htaccess` tweaks. + * + * @return bool True if removed successfully. + * + * @TODO Improve error reporting detail to better catch unexpected failures; see http://git.io/vEFLT + */ + public function removeWpHtaccess() + { + global $is_apache; + + if (!$is_apache) { + return false; // Not running the Apache web server. + } + if (!($htaccess_file = $this->findHtaccessFile())) { + return true; // File does not exist. + } + if (!$this->findHtaccessMarker()) { + return true; // Template blocks are already gone. + } + if (!($htaccess = $this->readHtaccessFile())) { + return false; // Failure; could not read file, create file, or invalid UTF8 encountered, file may be corrupt. + } + + $regex = '/#\s*BEGIN\s+'.preg_quote(NAME, '/').'\s+'.$this->htaccess_marker.'.*?#\s*END\s+'.preg_quote(NAME, '/').'\s+'.$this->htaccess_marker.'\s*/is'; + $htaccess['file_contents'] = preg_replace($regex, '', $htaccess['file_contents']); + + if (!$this->writeHtaccessFile($htaccess, false)) { + return false; // Failure; could not write changes. + } + + return true; // Removed successfully. + } + + /** + * Finds absolute server path to `/.htaccess` file. + * + * @since 151114 Adding `.htaccess` tweaks. + * + * @return string Absolute server path to `/.htaccess` file; + * else an empty string if unable to locate the file. + */ + public function findHtaccessFile() + { + $file = ''; // Initialize. + $home_path = $this->wpHomePath(); + + if (is_file($htaccess_file = $home_path.'.htaccess')) { + $file = $htaccess_file; + } + return $file; + } + + /** + * Determines if there are any plugin options enabled that require htaccess rules to be added. + * + * @since 160103 Improving `.htaccess` tweaks. + * + * @return bool True when an option is enabled that requires htaccess rules, false otherwise. + */ + public function needHtaccessRules() + { + if (!is_array($this->options_with_htaccess_rules)) { + return false; // Nothing to do. + } + foreach ($this->options_with_htaccess_rules as $option) { + if ($this->options[$option]) { + return true; // Yes, there are options enabled that require htaccess rules. + } + } + return false; // No, there are no options enabled that require htaccess rules. + } + + /** + * Utility method used to check if htaccess file contains $htaccess_marker. + * + * @since 151114 Adding `.htaccess` tweaks. + * + * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. + * + * @return bool False on failure or when marker does not exist in htaccess, true otherwise. + */ + public function findHtaccessMarker($htaccess_marker = '') + { + if (!($htaccess_file = $this->findHtaccessFile())) { + return false; // File does not exist. + } + if (!is_readable($htaccess_file)) { + return false; // Not possible. + } + if (($htaccess_file_contents = file_get_contents($htaccess_file)) === false) { + return false; // Failure; could not read file. + } + if (empty($htaccess_marker)) { + $htaccess_marker = $this->htaccess_marker; + } + if (stripos($htaccess_file_contents, $htaccess_marker) === false) { + return false; // Htaccess marker is missing + } + + return true; // Htaccess has the marker + } + + /** + * Gets contents of `/.htaccess` file with exclusive lock to read+write. If file doesn't exist, we attempt to create it. + * + * @since 151220 Improving `.htaccess` utils. + * + * @param string $htaccess_file Absolute path to the htaccess file. Optional. + * If not provided, we attempt to find it or create it if it doesn't exist. + * + * @return array|bool Returns an array with data necessary to call $this->writeHtaccessFile(): + * `fp` a file pointer resource, `file_contents` a string. Returns `false` on failure. + * + * @note If a call to this method is not followed by a call to $this->writeHtaccessFile(), + * you must make sure that you unlock and close the `fp` resource yourself. + */ + public function readHtaccessFile($htaccess_file = '') + { + if (empty($htaccess_file) && !($htaccess_file = $this->findHtaccessFile())) { + if (!is_writable($this->wpHomePath()) || file_put_contents($htaccess_file = $this->wpHomePath().'.htaccess', '') === false) { + return false; // Unable to find and/or create `.htaccess`. + } // If it doesn't exist, we create the `.htaccess` file here. + } + if (!is_readable($htaccess_file) || !is_writable($htaccess_file) || (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS)) { + return false; // Not possible. + } + if (!($fp = fopen($htaccess_file, 'rb+')) || !flock($fp, LOCK_EX)) { + fclose($fp); // Just in case we opened it before failing to obtain a lock. + return false; // Failure; could not open file and obtain an exclusive lock. + } + if (($file_contents = fread($fp, filesize($htaccess_file))) && ($file_contents === wp_check_invalid_utf8($file_contents))) { + rewind($fp); // Rewind pointer to beginning of file. + return compact('fp', 'file_contents'); + } else { // Failure; could not read file or invalid UTF8 encountered, file may be corrupt. + flock($fp, LOCK_UN); + fclose($fp); + return false; + } + } + + /** + * Writes to `/.htaccess` file using provided file pointer. + * + * @since 151220 Improving `.htaccess` utils. + * + * @param array $htaccess Array containing `fp` file resource pointing to htaccess file and `file_contents` to write to file. + * @param bool $require_marker Whether or not to require the marker be present in contents before writing. + * @param string $htaccess_marker Unique comment marker used to identify rules added by this plugin. + * + * @return bool True on success, false on failure. + */ + public function writeHtaccessFile(array $htaccess, $require_marker = true, $htaccess_marker = '') + { + if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { + return false; // Not possible. + } + if (!is_resource($htaccess['fp'])) { + return false; + } + $htaccess_marker = $htaccess_marker ?: $this->htaccess_marker; + + $_have_marker = stripos($htaccess['file_contents'], $htaccess_marker); + + // Note: rewind() necessary here because we fread() above. + if (($require_marker && $_have_marker === false) || !rewind($htaccess['fp']) || !ftruncate($htaccess['fp'], 0) || !fwrite($htaccess['fp'], $htaccess['file_contents'])) { + flock($htaccess['fp'], LOCK_UN); + fclose($htaccess['fp']); + return false; // Failure; could not write changes. + } + fflush($htaccess['fp']); + flock($htaccess['fp'], LOCK_UN); + fclose($htaccess['fp']); + + return true; + } + + /** + * Utility method used to unlock and close htaccess file resource. + * + * @since 151114 Adding `.htaccess` tweaks. + * + * @param array $htaccess Array containing at least an `fp` file resource pointing to htaccess file. + * + * @return bool False on failure, true otherwise. + */ + public function closeHtaccessFile(array $htaccess) + { + if (!is_resource($htaccess['fp'])) { + return false; // Failure; requires a valid file resource. + } + flock($htaccess['fp'], LOCK_UN); + fclose($htaccess['fp']); + + return true; + } +} diff --git a/src/includes/traits/Plugin/InstallUtils.php b/src/includes/traits/Plugin/InstallUtils.php new file mode 100644 index 0000000..79551fe --- /dev/null +++ b/src/includes/traits/Plugin/InstallUtils.php @@ -0,0 +1,553 @@ +setup(); // Ensure setup is complete. + + if (defined('WP_CLI') && WP_CLI) { + $this->updateOptions(['enable' => '1']); + } + if (!$this->options['welcomed'] && !$this->options['enable']) { + $settings_url = add_query_arg(urlencode_deep(['page' => GLOBAL_NS]), network_admin_url('/admin.php')); + $this->enqueueMainNotice(sprintf(__('%1$s successfully installed! :-) Please enable caching and review options.', 'comet-cache'), esc_html(NAME), esc_attr($settings_url)), ['push_to_top' => true]); + $this->updateOptions(['welcomed' => '1']); + } + if (!$this->options['enable']) { + return; // Nothing to do. + } + $this->addWpCacheToWpConfig(); + $this->addWpHtaccess(); + $this->addAdvancedCache(); + $this->updateBlogPaths(); + $this->autoClearCache(); + } + + /** + * Check current plugin version that is installed in WP. + * + * @since 150422 Rewrite. + * + * @attaches-to `admin_init` hook. + */ + public function checkVersion() + { + $prev_version = $this->options['version']; + if (version_compare($prev_version, VERSION, '>=')) { + return; // Nothing to do; up-to-date. + } + $this->updateOptions(['version' => VERSION]); + + new Classes\VsUpgrades($prev_version); + + if ($this->options['enable']) { + $this->addWpCacheToWpConfig(); + $this->addWpHtaccess(); + $this->addAdvancedCache(); + $this->updateBlogPaths(); + } + $this->wipeCache(); // Fresh start now. + + $this->enqueueMainNotice(sprintf(__('%1$s: detected a new version of itself. Recompiling w/ latest version... wiping the cache... all done :-)', 'comet-cache'), esc_html(NAME)), ['push_to_top' => true]); + } + + /** + * Plugin deactivation hook. + * + * @since 150422 Rewrite. + * + * @attaches-to {@link \register_deactivation_hook()} + */ + public function deactivate() + { + $this->setup(); // Ensure setup is complete. + + $this->removeWpCacheFromWpConfig(); + $this->removeWpHtaccess(); + $this->removeAdvancedCache(); + $this->clearCache(); + $this->resetCronSetup(); + } + + /** + * Plugin uninstall hook. + * + * @since 150422 Rewrite. + */ + public function uninstall() + { + $this->setup(); // Ensure setup is complete. + + if (!defined('WP_UNINSTALL_PLUGIN')) { + return; // Disallow. + } + if (empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) { + return; // Not uninstalling. + } + if (!current_user_can($this->uninstall_cap)) { + return; // Extra layer of security. + } + $this->removeWpCacheFromWpConfig(); + $this->removeWpHtaccess(); + $this->removeAdvancedCache(); + $this->wipeCache(); + $this->resetCronSetup(); + + if (!$this->options['uninstall_on_deletion']) { + return; // Nothing to do here. + } + $this->deleteAdvancedCache(); + $this->deleteBaseDir(); + + $wpdb = $this->wpdb(); // WordPress DB. + $like = '%'.$wpdb->esc_like(GLOBAL_NS).'%'; + + if (is_multisite()) { // Site options for a network installation. + $wpdb->query('DELETE FROM `'.esc_sql($wpdb->sitemeta).'` WHERE `meta_key` LIKE \''.esc_sql($like).'\''); + + switch_to_blog(get_current_site()->blog_id); // In case it started as a standard WP installation. + $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\''); + restore_current_blog(); // Restore current blog. + // + } else { // Standard WP installation. + $wpdb->query('DELETE FROM `'.esc_sql($wpdb->options).'` WHERE `option_name` LIKE \''.esc_sql($like).'\''); + } + } + + /** + * Adds `define('WP_CACHE', TRUE);` to the `/wp-config.php` file. + * + * @since 150422 Rewrite. + * + * @return string The new contents of the updated `/wp-config.php` file; + * else an empty string if unable to add the `WP_CACHE` constant. + */ + public function addWpCacheToWpConfig() + { + if (!$this->options['enable']) { + return ''; // Nothing to do. + } + if (!($wp_config_file = $this->findWpConfigFile())) { + return ''; // Unable to find `/wp-config.php`. + } + if (!is_readable($wp_config_file)) { + return ''; // Not possible. + } + if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { + return ''; // Failure; could not read file. + } + if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) { + return ''; // Failure; file empty + } + if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[1-9][0-9\.]*|TRUE|([\'"])(?:[^0\'"]|[^\'"]{2,})\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace)) { + return $wp_config_file_contents; // It's already in there; no need to modify this file. + } + if (!($wp_config_file_contents = $this->removeWpCacheFromWpConfig())) { + return ''; // Unable to remove previous value. + } + if (!($wp_config_file_contents = preg_replace('/^\s*(\<\?php|\<\?)\s+/i', '${1}'."\n"."define('WP_CACHE', TRUE);"."\n", $wp_config_file_contents, 1))) { + return ''; // Failure; something went terribly wrong here. + } + if (strpos($wp_config_file_contents, "define('WP_CACHE', TRUE);") === false) { + return ''; // Failure; unable to add; unexpected PHP code. + } + if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { + return ''; // We may NOT edit any files. + } + if (!is_writable($wp_config_file)) { + return ''; // Not possible. + } + if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { + return ''; // Failure; could not write changes. + } + return $wp_config_file_contents; + } + + /** + * Removes `define('WP_CACHE', TRUE);` from the `/wp-config.php` file. + * + * @since 150422 Rewrite. + * + * @return string The new contents of the updated `/wp-config.php` file; + * else an empty string if unable to remove the `WP_CACHE` constant. + */ + public function removeWpCacheFromWpConfig() + { + if (!($wp_config_file = $this->findWpConfigFile())) { + return ''; // Unable to find `/wp-config.php`. + } + if (!is_readable($wp_config_file)) { + return ''; // Not possible. + } + if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { + return ''; // Failure; could not read file. + } + if (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) { + return ''; // Failure; file empty + } + if (!preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents_no_whitespace)) { + return $wp_config_file_contents; // Already gone. + } + if (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:0|FALSE|NULL|([\'"])0?\\2)\s*\)\s*;/i', $wp_config_file_contents_no_whitespace) && !is_writable($wp_config_file)) { + return $wp_config_file_contents; // It's already disabled, and since we can't write to this file let's let this slide. + } + if (!($wp_config_file_contents = preg_replace('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[0-9\.]+|TRUE|FALSE|NULL|([\'"])[^\'"]*\\2)\s*\)\s*;/i', '', $wp_config_file_contents))) { + return ''; // Failure; something went terribly wrong here. + } + if (preg_match('/([\'"])WP_CACHE\\1/i', $wp_config_file_contents)) { + return ''; // Failure; perhaps the `/wp-config.php` file contains syntax we cannot remove safely. + } + if (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { + return ''; // We may NOT edit any files. + } + if (!is_writable($wp_config_file)) { + return ''; // Not possible. + } + if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { + return ''; // Failure; could not write changes. + } + return $wp_config_file_contents; + } + + /** + * Checks to make sure the `advanced-cache.php` file still exists; + * and if it doesn't, the `advanced-cache.php` is regenerated automatically. + * + * @since 150422 Rewrite. + * + * @attaches-to `init` hook. + * + * @note This runs so that remote deployments which completely wipe out an + * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache + * to stop functioning due to the lack of an `advanced-cache.php` file, which is generated by Comet Cache. + * + * For instance, if you have a Git repo with all of your site files; when you push those files + * to your website to deploy them, you most likely do NOT have the `advanced-cache.php` file. + * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active) + * we simply regenerate the file automatically to keep Comet Cache running. + */ + public function checkAdvancedCache() + { + if (!$this->options['enable']) { + return; // Nothing to do. + } + if (!empty($_REQUEST[GLOBAL_NS])) { + return; // Skip on plugin actions. + } + $cache_dir = $this->cacheDir(); + $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; + $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; + + // Fixes zero-byte advanced-cache.php bug related to migrating from ZenCache + // See: + + // Also fixes a missing `define('WP_CACHE', TRUE)` bug related to migrating from ZenCache + // See + + if (!is_file($advanced_cache_check_file) || !is_file($advanced_cache_file) || filesize($advanced_cache_file) === 0) { + $this->addAdvancedCache(); + $this->addWpCacheToWpConfig(); + } + } + + /** + * Creates and adds the `advanced-cache.php` file. + * + * @since 150422 Rewrite. + * + * @return bool|null `TRUE` on success. `FALSE` or `NULL` on failure. + * A special `NULL` return value indicates success with a single failure + * that is specifically related to the `[SHORT_NAME]-advanced-cache` file. + */ + public function addAdvancedCache() + { + if (!$this->removeAdvancedCache()) { + return false; // Still exists. + } + $cache_dir = $this->cacheDir(); + $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; + $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; + $advanced_cache_template = dirname(dirname(__DIR__)).'/templates/advanced-cache.txt'; + + if (is_file($advanced_cache_file) && !is_writable($advanced_cache_file)) { + return false; // Not possible to create. + } + if (!is_file($advanced_cache_file) && !is_writable(dirname($advanced_cache_file))) { + return false; // Not possible to create. + } + if (!is_file($advanced_cache_template) || !is_readable($advanced_cache_template)) { + return false; // Template file is missing; or not readable. + } + if (!($advanced_cache_contents = file_get_contents($advanced_cache_template))) { + return false; // Template file is missing; or is not readable. + } + $possible_advanced_cache_constant_key_values = array_merge( + $this->options, // The following additional keys are dynamic. + [ + 'cache_dir' => $this->basePathTo($this->cache_sub_dir), + + ] + ); + if ($this->applyWpFilters(GLOBAL_NS.'_exclude_uris_client_side_too', true)) { + $possible_advanced_cache_constant_key_values['exclude_client_side_uris'] .= "\n".$this->options['exclude_uris']; + } + foreach ($possible_advanced_cache_constant_key_values as $_option => $_value) { + $_value = (string) $_value; // Force string. + + switch ($_option) { + case 'exclude_uris': // Converts to regex (caSe insensitive). + case 'exclude_client_side_uris': // Converts to regex (caSe insensitive). + case 'exclude_refs': // Converts to regex (caSe insensitive). + case 'exclude_agents': // Converts to regex (caSe insensitive). + + + + $_value = "'".$this->escSq($this->lineDelimitedPatternsToRegex($_value))."'"; + + break; // Break switch handler. + + + + default: // Default case handler. + $_value = "'".$this->escSq($_value)."'"; + break; // Break switch handler. + } + $advanced_cache_contents = // Fill replacement codes. + str_ireplace( + [ + "'%%".GLOBAL_NS.'_'.$_option."%%'", + "'%%".GLOBAL_NS.'_'.preg_replace('/^cache_/i', '', $_option)."%%'", + ], + $_value, + $advanced_cache_contents + ); + } + unset($_option, $_value, $_values, $_response); // Housekeeping. + + if (strpos(PLUGIN_FILE, WP_CONTENT_DIR) === 0) { + $plugin_file = "WP_CONTENT_DIR.'".$this->escSq(str_replace(WP_CONTENT_DIR, '', PLUGIN_FILE))."'"; + } else { + $plugin_file = "'".$this->escSq(PLUGIN_FILE)."'"; // Full absolute path. + } + // Make it possible for the `advanced-cache.php` handler to find the plugin directory reliably. + $advanced_cache_contents = str_ireplace("'%%".GLOBAL_NS."_PLUGIN_FILE%%'", $plugin_file, $advanced_cache_contents); + + // Ignore; this is created by Comet Cache; and we don't need to obey in this case. + #if(defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) + # return FALSE; // We may NOT edit any files. + + if (!file_put_contents($advanced_cache_file, $advanced_cache_contents)) { + return false; // Failure; could not write file. + } + $cache_lock = $this->cacheLock(); // Lock cache. + + if (!is_dir($cache_dir)) { + mkdir($cache_dir, 0775, true); + } + if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) { + file_put_contents($cache_dir.'/.htaccess', $this->htaccess_deny); + } + if (!is_dir($cache_dir) || !is_writable($cache_dir) || !is_file($cache_dir.'/.htaccess') || !file_put_contents($advanced_cache_check_file, time())) { + $this->cacheUnlock($cache_lock); // Release. + return; // Special return value (NULL). + } + $this->cacheUnlock($cache_lock); // Release. + + $this->clearAcDropinFromOpcacheByForce(); + + return true; + } + + /** + * Removes the `advanced-cache.php` file. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` on success. `FALSE` on failure. + * + * @note The `advanced-cache.php` file is NOT actually deleted by this routine. + * Instead of deleting the file, we simply empty it out so that it's `0` bytes in size. + * + * The reason for this is to preserve any file permissions set by the site owner. + * If the site owner previously allowed this specific file to become writable, we don't want to + * lose that permission by deleting the file; forcing the site owner to do it all over again later. + * + * An example of where this is useful is when a site owner deactivates the CC plugin, + * but later they decide that CC really is the most awesome plugin in the world and they turn it back on. + */ + public function removeAdvancedCache() + { + $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; + + if (!is_file($advanced_cache_file)) { + return true; // Already gone. + } + if (is_readable($advanced_cache_file) && filesize($advanced_cache_file) === 0) { + return true; // Already gone; i.e. it's empty already. + } + if (!is_writable($advanced_cache_file)) { + return false; // Not possible. + } + /* Empty the file only. This way permissions are NOT lost in cases where + a site owner makes this specific file writable for Comet Cache. */ + if (file_put_contents($advanced_cache_file, '') !== 0) { + return false; // Failure. + } + $this->clearAcDropinFromOpcacheByForce(); + + return true; + } + + /** + * Deletes the `advanced-cache.php` file. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` on success. `FALSE` on failure. + * + * @note The `advanced-cache.php` file is deleted by this routine. + */ + public function deleteAdvancedCache() + { + $cache_dir = $this->cacheDir(); + $advanced_cache_file = WP_CONTENT_DIR.'/advanced-cache.php'; + $advanced_cache_check_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-advanced-cache'; + + if (is_file($advanced_cache_file)) { + if (!is_writable($advanced_cache_file) || !unlink($advanced_cache_file)) { + return false; // Not possible; or outright failure. + } + } + if (is_file($advanced_cache_check_file)) { + if (!is_writable($advanced_cache_check_file) || !unlink($advanced_cache_check_file)) { + return false; // Not possible; or outright failure. + } + } + $this->clearAcDropinFromOpcacheByForce(); + + return true; // Deletion success. + } + + /** + * Checks to make sure the `[SHORT_NAME]-blog-paths` file still exists; + * and if it doesn't, the `[SHORT_NAME]-blog-paths` file is regenerated automatically. + * + * @since 150422 Rewrite. + * + * @attaches-to `init` hook. + * + * @note This runs so that remote deployments which completely wipe out an + * existing set of website files (like the AWS Elastic Beanstalk does) will NOT cause Comet Cache + * to stop functioning due to the lack of a `[SHORT_NAME]-blog-paths` file, which is generated by Comet Cache. + * + * For instance, if you have a Git repo with all of your site files; when you push those files + * to your website to deploy them, you most likely do NOT have the `[SHORT_NAME]-blog-paths` file. + * Comet Cache creates this file on its own. Thus, if it's missing (and CC is active) + * we simply regenerate the file automatically to keep Comet Cache running. + */ + public function checkBlogPaths() + { + if (!$this->options['enable']) { + return; // Nothing to do. + } + if (!is_multisite()) { + return; // N/A. + } + if (!empty($_REQUEST[GLOBAL_NS])) { + return; // Skip on plugin actions. + } + $cache_dir = $this->cacheDir(); + $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths'; + + if (!is_file($blog_paths_file)) { + $this->updateBlogPaths(); + } + } + + /** + * Creates and/or updates the `[SHORT_NAME]-blog-paths` file. + * + * @since 150422 Rewrite. + * + * @attaches-to `enable_live_network_counts` filter. + * + * @param mixed $enable_live_network_counts Optional, defaults to a `NULL` value. + * + * @return mixed The value of `$enable_live_network_counts` (passes through). + * + * @note While this routine is attached to a WP filter, we also call upon it directly at times. + */ + public function updateBlogPaths($enable_live_network_counts = null) + { + $value = $enable_live_network_counts; // This hook actually rides on a filter. + + if (!$this->options['enable']) { + return $value; // Nothing to do. + } + if (!is_multisite()) { + return $value; // N/A. + } + $cache_dir = $this->cacheDir(); + $blog_paths_file = $cache_dir.'/'.strtolower(SHORT_NAME).'-blog-paths'; + + $cache_lock = $this->cacheLock(); + + if (!is_dir($cache_dir)) { + mkdir($cache_dir, 0775, true); + } + if (is_writable($cache_dir) && !is_file($cache_dir.'/.htaccess')) { + file_put_contents($cache_dir.'/.htaccess', $this->htaccess_deny); + } + if (is_dir($cache_dir) && is_writable($cache_dir)) { + $paths = // Collect child `[/base]/path/`s from the WordPress database. + $this->wpdb()->get_col('SELECT `path` FROM `'.esc_sql($this->wpdb()->blogs)."` WHERE `deleted` <= '0'"); + + $host_base_token = $this->hostBaseToken(); // Pull this once only. + + foreach ($paths as $_key => &$_path) { + if ($_path && $_path !== '/' && $host_base_token && $host_base_token !== '/') { + // Note that each `path` in the DB looks like: `[/base]/path/` (i.e., it includes base). + $_path = '/'.ltrim(preg_replace('/^'.preg_quote($host_base_token, '/').'/', '', $_path), '/'); + } + if (!$_path || $_path === '/') { + unset($paths[$_key]); // Exclude main site. + } + } + unset($_key, $_path); // Housekeeping. + + file_put_contents($blog_paths_file, serialize($paths)); + } + $this->cacheUnlock($cache_lock); // Release. + + return $value; // Pass through untouched (always). + } + + /** + * Deletes base directory. + * + * @since 151002 Improving multisite compat. + * + * @return int Total files removed by this routine (if any). + */ + public function deleteBaseDir() + { + $counter = 0; // Initialize. + + @set_time_limit(1800); // @TODO Display a warning. + + $counter += $this->deleteAllFilesDirsIn($this->wpContentBaseDirTo(''), true); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/MenuPageUtils.php b/src/includes/traits/Plugin/MenuPageUtils.php new file mode 100644 index 0000000..7157531 --- /dev/null +++ b/src/includes/traits/Plugin/MenuPageUtils.php @@ -0,0 +1,236 @@ +url('/src/client-s/css/menu-pages.min.css'), $deps, VERSION, 'all'); + } + + /** + * Adds JS for administrative menu pages. + * + * @since 150422 Rewrite. + * + * @attaches-to `admin_enqueue_scripts` hook. + */ + public function enqueueAdminScripts() + { + if (empty($_GET['page']) || strpos($_GET['page'], GLOBAL_NS) !== 0) { + return; // NOT a plugin page in the administrative area. + } + $deps = ['jquery', 'chartjs']; // Plugin dependencies. + + wp_enqueue_script('chartjs', set_url_scheme('//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js'), [], null, true); + wp_enqueue_script(GLOBAL_NS, $this->url('/src/client-s/js/menu-pages.min.js'), $deps, VERSION, true); + wp_localize_script( + GLOBAL_NS, + GLOBAL_NS.'_menu_page_vars', + [ + '_wpnonce' => wp_create_nonce(), + 'isMultisite' => is_multisite(), // Network? + 'currentUserHasCap' => current_user_can($this->cap), + 'currentUserHasNetworkCap' => current_user_can($this->network_cap), + 'htmlCompressorEnabled' => (boolean) $this->options['htmlc_enable'], + 'ajaxURL' => site_url('/wp-load.php', is_ssl() ? 'https' : 'http'), + 'emptyStatsCountsImageUrl' => $this->url('/src/client-s/images/stats-fc-empty.png'), + 'emptyStatsFilesImageUrl' => $this->url('/src/client-s/images/stats-fs-empty.png'), + 'i18n' => [ + 'name' => NAME, + 'perSymbol' => __('%', 'comet-cache'), + 'file' => __('file', 'comet-cache'), + 'files' => __('files', 'comet-cache'), + 'pageCache' => __('Page Cache', 'comet-cache'), + 'htmlCompressor' => __('HTML Compressor', 'comet-cache'), + 'currentTotal' => __('Current Total', 'comet-cache'), + 'currentSite' => __('Current Site', 'comet-cache'), + 'xDayHigh' => __('%s Day High', 'comet-cache'), + ], + ] + ); + } + + /** + * Creates network admin menu pages. + * + * @since 150422 Rewrite. + * + * @attaches-to `network_admin_menu` hook. + */ + public function addNetworkMenuPages() + { + if (!is_multisite()) { + return; // Not applicable. + } + $icon = file_get_contents(dirname(dirname(dirname(__DIR__))).'/client-s/images/inline-icon.svg'); + $icon = 'data:image/svg+xml;base64,'.base64_encode($this->colorSvgMenuIcon($icon)); + + add_menu_page(NAME.(IS_PRO ? ' Pro' : ''), NAME.(IS_PRO ? ' Pro' : ''), $this->network_cap, GLOBAL_NS, [$this, 'menuPageOptions'], $icon); + add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $this->network_cap, GLOBAL_NS, [$this, 'menuPageOptions']); + + + + + } + + /** + * Creates admin menu pages. + * + * @since 150422 Rewrite. + * + * @attaches-to `admin_menu` hook. + */ + public function addMenuPages() + { + if (is_multisite()) { + return; // Multisite networks MUST use network admin area. + } + $icon = file_get_contents(dirname(dirname(dirname(__DIR__))).'/client-s/images/inline-icon.svg'); + $icon = 'data:image/svg+xml;base64,'.base64_encode($this->colorSvgMenuIcon($icon)); + + add_menu_page(NAME.(IS_PRO ? ' Pro' : ''), NAME.(IS_PRO ? ' Pro' : ''), $this->cap, GLOBAL_NS, [$this, 'menuPageOptions'], $icon); + add_submenu_page(GLOBAL_NS, __('Plugin Options', 'comet-cache'), __('Plugin Options', 'comet-cache'), $this->cap, GLOBAL_NS, [$this, 'menuPageOptions']); + + + + + } + + /** + * Adds link(s) to Comet Cache row on the WP plugins page. + * + * @since 150422 Rewrite. + * + * @attaches-to `plugin_action_links_'.plugin_basename(PLUGIN_FILE)` filter. + * + * @param array $links An array of the existing links provided by WordPress. + * + * @return array Revised array of links. + */ + public function addSettingsLink($links) + { + if (is_multisite() && !is_network_admin()) { + return $links; + } + + $links[] = ''.__('Settings', 'comet-cache').''; + if (!IS_PRO) { + $links[] = '
'.__('Preview Pro Features', 'comet-cache').''; + $links[] = ''.__('Upgrade', 'comet-cache').''; + } + return $links; + } + + /** + * Fills menu page inline SVG icon color. + * + * @since 150422 Rewrite. + * + * @param string $svg Inline SVG icon markup. + * + * @return string Inline SVG icon markup. + */ + public function colorSvgMenuIcon($svg) + { + if (!($color = get_user_option('admin_color'))) { + $color = 'fresh'; // Default color scheme. + } + if (empty($this->wp_admin_icon_colors[$color])) { + return $svg; // Not possible. + } + $icon_colors = $this->wp_admin_icon_colors[$color]; + $use_icon_fill_color = $icon_colors['base']; // Default base. + + $current_pagenow = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : ''; + $current_page = !empty($_REQUEST['page']) ? $_REQUEST['page'] : ''; + + if (strpos($current_pagenow, GLOBAL_NS) === 0 || strpos($current_page, GLOBAL_NS) === 0) { + $use_icon_fill_color = $icon_colors['current']; + } + return str_replace(' fill="currentColor"', ' fill="'.esc_attr($use_icon_fill_color).'"', $svg); + } + + /** + * Loads the admin menu page options. + * + * @since 150422 Rewrite. + */ + public function menuPageOptions() + { + new Classes\MenuPage('options'); + } + + + + + + /** + * WordPress admin icon color schemes. + * + * @since 150422 Rewrite. + * + * @type array WP admin icon colors. + * + * @note These must be hard-coded, because they don't become available + * in core until `admin_init`; i.e., too late for `admin_menu`. + */ + public $wp_admin_icon_colors = [ + 'fresh' => ['base' => '#999999', 'focus' => '#2EA2CC', 'current' => '#FFFFFF'], + 'light' => ['base' => '#999999', 'focus' => '#CCCCCC', 'current' => '#CCCCCC'], + 'blue' => ['base' => '#E5F8FF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + 'midnight' => ['base' => '#F1F2F3', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + 'sunrise' => ['base' => '#F3F1F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + 'ectoplasm' => ['base' => '#ECE6F6', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + 'ocean' => ['base' => '#F2FCFF', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + 'coffee' => ['base' => '#F3F2F1', 'focus' => '#FFFFFF', 'current' => '#FFFFFF'], + ]; + + /** + * On a specific menu page? + * + * @since 151002 Improving multisite compat. + * + * @param string $which Which page to check; may contain wildcards. + * + * @return bool True if is the menu page. + */ + public function isMenuPage($which) + { + if (!($which = trim((string) $which))) { + return false; // Empty. + } + if (!is_admin()) { + return false; + } + $page = $pagenow = ''; // Initialize. + + if (!empty($_REQUEST['page'])) { + $page = (string) $_REQUEST['page']; + } + if (!empty($GLOBALS['pagenow'])) { + $pagenow = (string) $GLOBALS['pagenow']; + } + if ($page && fnmatch($which, $page, FNM_CASEFOLD)) { + return true; // Wildcard match. + } + if ($pagenow && fnmatch($which, $pagenow, FNM_CASEFOLD)) { + return true; // Wildcard match. + } + return false; // Nope. + } +} diff --git a/src/includes/traits/Plugin/NoticeUtils.php b/src/includes/traits/Plugin/NoticeUtils.php new file mode 100644 index 0000000..02da8c0 --- /dev/null +++ b/src/includes/traits/Plugin/NoticeUtils.php @@ -0,0 +1,337 @@ + $notice]; + $notice = $this->normalizeNotice($notice, $args); + $key = sha1(serialize($notice)); // Prevent dupes. + + $notices = $this->getNotices($blog_id); + + if ($notice['push_to_top']) { + $notices = [$key => $notice] + $notices; + } else { + $notices[$key] = $notice; // Default behavior. + } + $this->updateNotices($notices, $blog_id); + + return $key; // For dismissals. + } + + /** + * Dismiss an administrative notice. + * + * @since 151002 Improving multisite compat. + * + * @param string $key_to_dismiss A unique key which identifies a particular notice. + * Or, a persistent key which identifies one or more persistent notices. + * @param int $blog_id The blog ID from which to dismiss the notice. + * + * @return array All remaining notices. + */ + public function dismissNotice($key_to_dismiss, $blog_id = 0) + { + $key_to_dismiss = trim((string) $key_to_dismiss); + $blog_id = (integer) $blog_id; // For multisite compat. + $notices = $enqueued_notices = $this->getNotices($blog_id); + + if (!$key_to_dismiss) { + return $notices; // Nothing to do. + } + foreach ($notices as $_key => $_notice) { + if ($_key === $key_to_dismiss) { + unset($notices[$_key]); // A specific key. + } elseif ($_notice['persistent_key'] === $key_to_dismiss) { + unset($notices[$_key]); // All matching keys. + } + } // ↑ Dismisses all matching keys. + unset($_key, $_notice); // Housekeeping. + + if ($notices !== $enqueued_notices) { // Something changed? + $this->updateNotices($notices, $blog_id); // Update. + } + return $notices; // All remaining notices. + } + + /** + * Enqueue an administrative error notice. + * + * @since 150422 Rewrite. Improved 151002. + */ + public function enqueueError($notice, array $args = [], $blog_id = 0) + { + return $this->enqueueNotice($notice, array_merge($args, ['class' => 'error']), $blog_id); + } + + /** + * Enqueue an administrative notice (main site). + * + * @since 151002. Improving multisite compat. + */ + public function enqueueMainNotice($notice, array $args = []) + { + return $this->enqueueNotice($notice, $args, -1); + } + + /** + * Enqueue an administrative error notice (main site). + * + * @since 151002. Improving multisite compat. + */ + public function enqueueMainError($notice, array $args = []) + { + return $this->enqueueNotice($notice, array_merge($args, ['class' => 'error']), -1); + } + + /** + * Dismiss an administrative notice (main site). + * + * @since 151002 Improving multisite compat. + */ + public function dismissMainNotice($key_to_dismiss) + { + return $this->dismissNotice($key_to_dismiss, -1); + } + + /* + * Notice display handler. + */ + + /** + * Render admin notices. + * + * @since 150422 Rewrite. Improved 151002. + * + * @attaches-to `all_admin_notices` hook. + */ + public function allAdminNotices() + { + $notices = $enqueued_notices = $this->getNotices(); + $combined_notices = []; // Initialize + + foreach ($notices as $_key => $_notice) { + # Always dismiss all non-persistent transients. + + if ($_notice['is_transient'] && !$_notice['persistent_key']) { + unset($notices[$_key]); // Dismiss. + } + # Current user can see this notice? + + if (!current_user_can($this->cap)) { + continue; // Current user unable to see. + } + if ($_notice['cap_required'] && !current_user_can($_notice['cap_required'])) { + continue; // Current user unable to see this notice. + } + # Current URI matches a limited scope/context for this notice? + + if ($_notice['only_on_uris'] && !@preg_match($_notice['only_on_uris'], $_SERVER['REQUEST_URI'])) { + continue; // Not in the right context at the moment; i.e., does not regex. + } + # If persistent, allow a site owner to dismiss. + + $_dismiss = ''; // Initialize + if ($_notice['persistent_key'] && $_notice['dismissable']) { // See above. The `dismissNotice()` action requires `$this->cap` always. + $_dismiss = add_query_arg(urlencode_deep([GLOBAL_NS => ['dismissNotice' => ['key' => $_key]], '_wpnonce' => wp_create_nonce()])); + $_dismiss = ''; + } + # Display this notice, or save for displaying compacted later. If not persistent, we can dismiss it too. + + if ($_notice['combinable'] && !$_notice['persistent_key']) { + $combined_notices[] = $_notice['notice']; // Save this for displaying as part of a single, combined notice. + } else { + echo '

'.$_notice['notice'].'

'.$_dismiss.'
'; + } + + if (!$_notice['persistent_key']) { // If not persistent, dismiss. + unset($notices[$_key]); // Dismiss; this notice has been displayed now. + } + } + unset($_key, $_notice, $_dismiss); // Housekeeping. + + if (!empty($combined_notices)) { // Display a single notice with details hidden by default. + $_line_items = ''; // Initialize + foreach ($combined_notices as $_item) { + $_line_items .= '

'.$_item.'

'."\n"; + } + + $_show_details = __('Show details.', 'comet-cache'); + $_hide_details = __('Hide details.', 'comet-cache'); + + $_combined = '
'; + $_combined .= '

'; + $_combined .= sprintf(__('%1$s detected changes and intelligently cleared the cache to keep your site up-to-date. '.$_show_details.'', 'comet-cache'), esc_html(NAME)).'

'; + $_combined .= ''; + $_combined .= ''; + $_combined .= '
'; + + echo $_combined; + + unset($_item, $_line_item, $_combined); // Housekeeping. + } + + # Update notices if something changed above. + + if ($notices !== $enqueued_notices) { // Something changed? + $this->updateNotices($notices); // Update. + } + } + + /* + * Notice getter/setter. + */ + + /** + * Get admin notices. + * + * @since 151002 Improving multisite compat. + * + * @param int $blog_id Optional. Defaults to the current blog ID. + * Use any value `< 0` to indicate the main site. + * + * @return array All notices. + */ + public function getNotices($blog_id = 0) + { + if (is_multisite()) { + if (!($blog_id = (integer) $blog_id)) { + $blog_id = (integer) get_current_blog_id(); + } + if ($blog_id < 0) { // Blog for main site. + $blog_id = (integer) get_current_site()->blog_id; + } + $blog_suffix = '_'.$blog_id; // Site option suffix. + $notices = get_site_option(GLOBAL_NS.$blog_suffix.'_notices'); + } else { + $notices = get_site_option(GLOBAL_NS.'_notices'); + } + if (!is_array($notices)) { + $notices = []; // Force array. + // Prevent multiple DB queries by adding this key. + $this->updateNotices($notices, $blog_id); + } + foreach ($notices as $_key => &$_notice) { + if (!is_string($_key) || !is_array($_notice) || empty($_notice['notice'])) { + unset($notices[$_key]); // Old notice. + continue; // Bypass; i.e., do not normalize. + } + $_notice = $this->normalizeNotice($_notice); + } // ↑ Typecast/normalized each of the array elements. + unset($_key, $_notice); // Housekeeping. + + return $notices; + } + + /** + * Update admin notices. + * + * @since 151002 Improving multisite compat. + * + * @param array $notices New array of notices. + * @param int $blog_id Optional. Defaults to the current blog ID. + * Use any value `< 0` to indicate the main site. + * + * @return array All notices. + */ + public function updateNotices(array $notices, $blog_id = 0) + { + if (is_multisite()) { + if (!($blog_id = (integer) $blog_id)) { + $blog_id = (integer) get_current_blog_id(); + } + if ($blog_id < 0) { // Blog for main site. + $blog_id = (integer) get_current_site()->blog_id; + } + $blog_suffix = '_'.$blog_id; // Site option suffix. + update_site_option(GLOBAL_NS.$blog_suffix.'_notices', $notices); + } else { + update_site_option(GLOBAL_NS.'_notices', $notices); + } + return $notices; + } + + /* + * Notice property utilities. + */ + + /** + * Normalize notice elements. + * + * @since 151002 Improving multisite compat. + * + * @param array $notice Notice array elements. + * @param array $args Any additional array elements. + * + * @return array Normalized notice array elements. + */ + public function normalizeNotice(array $notice, array $args = []) + { + $notice_defaults = [ + 'notice' => '', + 'only_on_uris' => '', + 'persistent_key' => '', + 'combinable' => false, + 'dismissable' => true, + 'is_transient' => true, + 'push_to_top' => false, + 'class' => 'updated', + 'cap_required' => '', // `$this->cap` always. + // i.e., this cap is in addition to `$this->cap`. + ]; + $notice = array_merge($notice_defaults, $notice, $args); + $notice = array_intersect_key($notice, $notice_defaults); + + foreach ($notice as $_key => &$_value) { + switch ($_key) { + case 'notice': + case 'only_on_uris': + case 'persistent_key': + $_value = trim((string) $_value); + break; // Stop here. + + case 'is_transient': + case 'push_to_top': + case 'combinable': + case 'dismissable': + $_value = (boolean) $_value; + break; // Stop here. + + case 'class': + case 'cap_required': + $_value = trim((string) $_value); + break; // Stop here. + } + } // ↑ Typecast each of the array elements. + unset($_key, $_value); // A little housekeeping. + + ksort($notice); // For more accurate comparison in other routines. + + return $notice; // Normalized. + } +} diff --git a/src/includes/traits/Plugin/OptionUtils.php b/src/includes/traits/Plugin/OptionUtils.php new file mode 100644 index 0000000..5a2f6f4 --- /dev/null +++ b/src/includes/traits/Plugin/OptionUtils.php @@ -0,0 +1,81 @@ +options)) { // Not defined yet? + if (!is_array($options = get_site_option(GLOBAL_NS.'_options'))) { + $options = []; // Force array. + } + if (!$options && is_array($zencache_options = get_site_option('zencache_options'))) { + $options = $zencache_options; // Old ZenCache options. + $options['crons_setup'] = $this->default_options['crons_setup']; + $options['latest_lite_version'] = $this->default_options['latest_lite_version']; + $options['latest_pro_version'] = $this->default_options['latest_pro_version']; + } + } + $this->options = array_merge($this->default_options, $options); + $this->options = $this->applyWpFilters(GLOBAL_NS.'_options', $this->options); + $this->options = array_intersect_key($this->options, $this->default_options); + + foreach ($this->options as $_key => &$_value) { + $_value = trim((string) $_value); // Force strings. + } + unset($_key, $_value); // Housekeeping. + + $this->options['base_dir'] = trim($this->options['base_dir'], '\\/'." \t\n\r\0\x0B"); + if (!$this->options['base_dir'] || strpos(basename($this->options['base_dir']), 'wp-') === 0) { + $this->options['base_dir'] = $this->default_options['base_dir']; + } + return $this->options; // Plugin options. + } + + /** + * Update plugin options. + * + * @since 151002 Improving multisite compat. + * + * @param array $options One or more new options. + * + * @return array Plugin options after update. + */ + public function updateOptions(array $options) + { + if (!IS_PRO) { // Do not save Pro option keys. + $options = array_diff_key($options, $this->pro_only_option_keys); + } + if (!empty($options['base_dir']) && $options['base_dir'] !== $this->options['base_dir']) { + $this->tryErasingAllFilesDirsIn($this->wpContentBaseDirTo('')); + } + $this->options = array_merge($this->default_options, $this->options, $options); + $this->options = array_intersect_key($this->options, $this->default_options); + update_site_option(GLOBAL_NS.'_options', $this->options); + + return $this->getOptions(); + } + + /** + * Restore default plugin options. + * + * @since 151002 Improving multisite compat. + * + * @return array Plugin options after update. + */ + public function restoreDefaultOptions() + { + delete_site_option(GLOBAL_NS.'_options'); // Force restore. + $this->options = $this->default_options; // In real-time. + return $this->getOptions(); + } +} diff --git a/src/includes/traits/Plugin/PostUtils.php b/src/includes/traits/Plugin/PostUtils.php new file mode 100644 index 0000000..623b169 --- /dev/null +++ b/src/includes/traits/Plugin/PostUtils.php @@ -0,0 +1,49 @@ +cacheKey('postStatuses'))) { + return $statuses; // Already did this. + } + $statuses = get_post_stati(); + $statuses = array_keys($statuses); + + return $statuses; + } + + /** + * All built-in post statuses. + * + * @since 150821 Improving bbPress support. + * + * @return array All built-in post statuses. + */ + public function builtInPostStatuses() + { + if (!is_null($statuses = &$this->cacheKey('builtInPostStatuses'))) { + return $statuses; // Already did this. + } + $statuses = []; // Initialize. + + foreach (get_post_stati([], 'objects') as $_key => $_status) { + if (!empty($_status->_builtin)) { + $statuses[] = $_status->name; + } + } + unset($_key, $_status); // Housekeeping. + + return $statuses; + } +} diff --git a/src/includes/traits/Plugin/UpdateUtils.php b/src/includes/traits/Plugin/UpdateUtils.php new file mode 100644 index 0000000..22aa88a --- /dev/null +++ b/src/includes/traits/Plugin/UpdateUtils.php @@ -0,0 +1,52 @@ +options['lite_update_check']) { + return; // Nothing to do. + } + if (!current_user_can($this->update_cap)) { + return; // Nothing to do. + } + if (is_multisite() && !current_user_can($this->network_cap)) { + return; // Nothing to do. + } + if ($this->options['last_lite_update_check'] >= strtotime('-1 hour')) { + return; // No reason to keep checking on this. + } + $this->updateOptions(['last_lite_update_check' => time()]); + + $product_api_url = 'https://'.urlencode(DOMAIN).'/'; + $product_api_input_vars = ['product_api' => ['action' => 'latest_lite_version']]; + + $product_api_response = wp_remote_post($product_api_url, ['body' => $product_api_input_vars]); + $product_api_response = json_decode(wp_remote_retrieve_body($product_api_response)); + + if (is_object($product_api_response) && !empty($product_api_response->lite_version)) { + $this->updateOptions(['latest_lite_version' => $product_api_response->lite_version]); + } + // Disabling the notice for now. We only run this check to collect the latest version number. + #if ($this->options['latest_lite_version'] && version_compare(VERSION, $this->options['latest_lite_version'], '<')) { + # $this->dismissMainNotice('new-lite-version-available'); // Dismiss any existing notices like this. + # $lite_updater_page = network_admin_url('/plugins.php'); // In a network this points to the master plugins list. + # $this->enqueueMainNotice(sprintf(__('%1$s: a new version is now available. Please upgrade to v%3$s.', 'comet-cache'), esc_html(NAME), esc_attr($lite_updater_page), esc_html($this->options['latest_lite_version'])), array('persistent_key' => 'new-lite-version-available')); + #} + } + + +} diff --git a/src/includes/traits/Plugin/UrlUtils.php b/src/includes/traits/Plugin/UrlUtils.php new file mode 100644 index 0000000..99550ae --- /dev/null +++ b/src/includes/traits/Plugin/UrlUtils.php @@ -0,0 +1,52 @@ +cacheKey('currentUserCanClearCache'))) { + return $can; // Already cached this. + } + $is_multisite = is_multisite(); + + if (!$is_multisite && current_user_can($this->cap)) { + return $can = true; // Plugin admin. + } + if ($is_multisite && current_user_can($this->network_cap)) { + return $can = true; // Plugin admin. + } + + return $can = false; + } + + /** + * Alias for currentUserCanClearCache(). + * + * @since 151002 Enhancing user permissions. + * + * @return bool Current user can clear the cache? + */ + public function currentUserCanWipeCache() + { + return call_user_func_array([$this, 'currentUserCanClearCache'], func_get_args()); + } + + /** + * Current user can clear the opcache? + * + * @since 151114 Enhancing user permissions. + * + * @return bool Current user can clear the opcache? + */ + public function currentUserCanClearOpCache() + { + if (!is_null($can = &$this->cacheKey('currentUserCanClearOpCache'))) { + return $can; // Already cached this. + } + $is_multisite = is_multisite(); + + if (!$is_multisite && current_user_can($this->cap)) { + return $can = true; // Plugin admin. + } + if ($is_multisite && current_user_can($this->network_cap)) { + return $can = true; // Plugin admin. + } + return $can = false; + } + + /** + * Alias for currentUserCanClearOpCache(). + * + * @since 151114 Enhancing user permissions. + * + * @return bool Current user can clear the opcache? + */ + public function currentUserCanWipeOpCache() + { + return call_user_func_array([$this, 'currentUserCanClearOpCache'], func_get_args()); + } + + /** + * Current user can clear the CDN cache? + * + * @since 151114 Enhancing user permissions. + * + * @return bool Current user can clear the CDN cache? + */ + public function currentUserCanClearCdnCache() + { + if (!is_null($can = &$this->cacheKey('currentUserCanClearCdnCache'))) { + return $can; // Already cached this. + } + $is_multisite = is_multisite(); + + if (!$is_multisite && current_user_can($this->cap)) { + return $can = true; // Plugin admin. + } + if ($is_multisite && current_user_can($this->network_cap)) { + return $can = true; // Plugin admin. + } + return $can = false; + } + + /** + * Alias for currentUserCanClearCdnCache(). + * + * @since 151114 Enhancing user permissions. + * + * @return bool Current user can clear the CDN cache? + */ + public function currentUserCanWipeCdnCache() + { + return call_user_func_array([$this, 'currentUserCanClearCdnCache'], func_get_args()); + } + + /** + * Current user can clear expired transients? + * + * @since 151220 Enhancing user permissions. + * + * @return bool Current user can clear expired transients? + */ + public function currentUserCanClearExpiredTransients() + { + if (!is_null($can = &$this->cacheKey('currentUserCanClearExpiredTransients'))) { + return $can; // Already cached this. + } + $is_multisite = is_multisite(); + + if (!$is_multisite && current_user_can($this->cap)) { + return $can = true; // Plugin admin. + } + if ($is_multisite && current_user_can($this->network_cap)) { + return $can = true; // Plugin admin. + } + return $can = false; + } + + /** + * Alias for currentUserCanClearExpiredTransients(). + * + * @since 151220 Enhancing user permissions. + * + * @return bool Current user can clear expired transients? + */ + public function currentUserCanWipeExpiredTransients() + { + return call_user_func_array([$this, 'currentUserCanClearExpiredTransients'], func_get_args()); + } + + /** + * Current user can see stats? + * + * @since 151002 Enhancing user permissions. + * + * @return bool Current user can see stats? + */ + public function currentUserCanSeeStats() + { + if (!is_null($can = &$this->cacheKey('currentUserCanSeeStats'))) { + return $can; // Already cached this. + } + $is_multisite = is_multisite(); + + if (!$is_multisite && current_user_can($this->cap)) { + return $can = true; // Plugin admin. + } + if ($is_multisite && current_user_can($this->network_cap)) { + return $can = true; // Plugin admin. + } + + return $can = false; + } +} diff --git a/src/includes/traits/Plugin/WcpAuthorUtils.php b/src/includes/traits/Plugin/WcpAuthorUtils.php new file mode 100644 index 0000000..4130342 --- /dev/null +++ b/src/includes/traits/Plugin/WcpAuthorUtils.php @@ -0,0 +1,97 @@ +cacheKey('autoClearAuthorPageCache', [$post_id, $post_after->ID, $post_before->ID]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_author_page_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + /* + * If we're changing the post author AND + * the previous post status was either 'published' or 'private' + * then clear the author page for both authors. + * + * Else if the old post status was 'published' or 'private' OR + * the new post status is 'published' or 'private' + * then clear the author page for the current author. + * + * Else return the counter; post status does not warrant clearing author page cache. + */ + if ($post_after->post_author !== $post_before->post_author && + ($post_before->post_status === 'publish' || $post_before->post_status === 'private') + ) { + $authors[] = (integer) $post_before->post_author; + $authors[] = (integer) $post_after->post_author; + } elseif (($post_before->post_status === 'publish' || $post_before->post_status === 'private') || + ($post_after->post_status === 'publish' || $post_after->post_status === 'private') + ) { + $authors[] = (integer) $post_after->post_author; + } + if (!$authors) { + return $counter; // Nothing to do. + } + foreach ($authors as $_author_id) { + $authors_to_clear[$_author_id]['posts_url'] = get_author_posts_url($_author_id); + $authors_to_clear[$_author_id]['display_name'] = get_the_author_meta('display_name', $_author_id); + } + unset($_author_id); // Housekeeping. + + foreach ($authors_to_clear as $_author) { + $_author_regex = $this->buildHostCachePathRegex($_author['posts_url']); + $_author_counter = $this->clearFilesFromHostCacheDir($_author_regex); + $counter += $_author_counter; // Add to overall counter. + + if ($_author_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for Author Page: %2$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($_author_counter)), esc_html($_author['display_name'])), ['combinable' => true]); + ++$enqueued_notices; // Increment enqueued notices counter. + } + } + unset($_author, $_author_regex, $_author_counter); // Housekeeping. + + $counter += $this->autoClearXmlFeedsCache('blog'); + $counter += $this->autoClearXmlFeedsCache('post-authors', $post_id); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpCommentUtils.php b/src/includes/traits/Plugin/WcpCommentUtils.php new file mode 100644 index 0000000..388c4c2 --- /dev/null +++ b/src/includes/traits/Plugin/WcpCommentUtils.php @@ -0,0 +1,103 @@ +cacheKey('autoClearCommentPostCache', $comment_id))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!is_object($comment = get_comment($comment_id))) { + return $counter; // Nothing we can do. + } + if (empty($comment->comment_post_ID)) { + return $counter; // Nothing we can do. + } + if ($comment->comment_approved === 'spam' || $comment->comment_approved === '0') { + // Don't allow next `autoClearPostCache()` call to clear post cache. + $allow = &$this->cacheKey('autoClearPostCache_allow'); + $allow = false; // Flag as false; i.e., disallow. + return $counter; + } + $counter += $this->autoClearXmlFeedsCache('blog-comments'); + $counter += $this->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID); + $counter += $this->autoClearPostCache($comment->comment_post_ID); + + return $counter; + } + + /** + * Automatically clears cache files for a post associated with a particular comment. + * + * @since 150422 Rewrite. + * + * @attaches-to `transition_comment_status` hook. + * + * @param string $new_status New comment status. + * @param string $old_status Old comment status. + * @param \stdClass $comment Comment object. + * + * @throws \Exception If a clear failure occurs. + * + * @return int Total files cleared by this routine (if any). + * + * @note This is also called upon by other routines which listen for + * events that are indirectly associated with a comment ID. + */ + public function autoClearCommentPostCacheTransition($new_status, $old_status, $comment) + { + $counter = 0; // Initialize. + + if (!is_object($comment)) { + return $counter; // Nothing we can do. + } + if (empty($comment->comment_post_ID)) { + return $counter; // Nothing we can do. + } + if (!is_null($done = &$this->cacheKey('autoClearCommentPostCacheTransition', [$new_status, $old_status, $comment->comment_post_ID]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!($old_status === 'approved' || ($old_status === 'unapproved' && $new_status === 'approved'))) { + // If excluded here, don't allow next `autoClearPostCache()` call to clear post cache. + $allow = &$this->cacheKey('autoClearPostCache_allow'); + $allow = false; // Flag as false; i.e., disallow. + return $counter; + } + $counter += $this->autoClearXmlFeedsCache('blog-comments'); + $counter += $this->autoClearXmlFeedsCache('post-comments', $comment->comment_post_ID); + $counter += $this->autoClearPostCache($comment->comment_post_ID); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpFeedUtils.php b/src/includes/traits/Plugin/WcpFeedUtils.php new file mode 100644 index 0000000..c381222 --- /dev/null +++ b/src/includes/traits/Plugin/WcpFeedUtils.php @@ -0,0 +1,121 @@ +cacheKey('autoClearXmlFeedsCache', [$type, $post_id]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['feeds_enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_xml_feeds_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $utils = new Classes\FeedUtils(); // Feed utilities. + $variations = $variation_regex_frags = []; // Initialize. + + switch ($type) { // Handle clearing based on the `$type`. + + case 'blog': // The blog feed; i.e. `/feed/` on most WP installs. + $variations = array_merge($variations, $utils->feedLinkVariations()); + break; // Break switch handler. + + case 'blog-comments': // The blog comments feed; i.e. `/comments/feed/` on most WP installs. + $variations = array_merge($variations, $utils->feedLinkVariations('comments_')); + break; // Break switch handler. + + case 'post-comments': // Feeds related to comments that a post has. + if (!$post_id) { + break; // Break switch handler. + } + if (!($post = get_post($post_id))) { + break; // Break switch handler. + } + $variations = array_merge($variations, $utils->postCommentsFeedLinkVariations($post)); + break; // Break switch handler. + + case 'post-authors': // Feeds related to authors that a post has. + if (!$post_id) { + break; // Break switch handler. + } + if (!($post = get_post($post_id))) { + break; // Break switch handler. + } + $variations = array_merge($variations, $utils->postAuthorFeedLinkVariations($post)); + break; // Break switch handler. + + case 'post-terms': // Feeds related to terms that a post has. + if (!$post_id) { + break; // Break switch handler. + } + if (!($post = get_post($post_id))) { + break; // Break switch handler. + } + $variations = array_merge($variations, $utils->postTermFeedLinkVariations($post, true)); + break; // Break switch handler. + + case 'custom-post-type': // Feeds related to a custom post type archive view. + if (!$post_id) { + break; // Break switch handler. + } + if (!($post = get_post($post_id))) { + break; // Break switch handler. + } + $variations = array_merge($variations, $utils->postTypeArchiveFeedLinkVariations($post)); + break; // Break switch handler. + + // @TODO Possibly consider search-related feeds in the future. + // See: + } + if (!($variation_regex_frags = $utils->convertVariationsToHostCachePathRegexFrags($variations))) { + return $counter; // Nothing to do here. + } + $in_sets_of = $this->applyWpFilters(GLOBAL_NS.'_autoClearXmlFeedsCache_in_sets_of', 10, get_defined_vars()); + for ($_i = 0; $_i < count($variation_regex_frags); $_i = $_i + $in_sets_of) { + $_variation_regex_frags = array_slice($variation_regex_frags, $_i, $in_sets_of); + $_regex = '/^\/(?:'.implode('|', $_variation_regex_frags).')\./i'; + $counter += $this->clearFilesFromHostCacheDir($_regex); + } + unset($_i, $_variation_regex_frags, $_regex); // Housekeeping. + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache, for XML feeds of type: %2$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter)), esc_html($type)), ['combinable' => true]); + } + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpHomeBlogUtils.php b/src/includes/traits/Plugin/WcpHomeBlogUtils.php new file mode 100644 index 0000000..08bfe55 --- /dev/null +++ b/src/includes/traits/Plugin/WcpHomeBlogUtils.php @@ -0,0 +1,106 @@ +cacheKey('autoClearHomePageCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_home_page_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $regex = $this->buildHostCachePathRegex(home_url('/')); + $counter += $this->clearFilesFromHostCacheDir($regex); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for the designated "Home Page"; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + $counter += $this->autoClearXmlFeedsCache('blog'); + + return $counter; + } + + /** + * Automatically clears cache files for the posts page. + * + * @since 150422 Rewrite. + * + * @throws \Exception If a clear failure occurs. + * + * @return int Total files cleared by this routine (if any). + * + * @note Unlike many of the other `auto_` methods, this one is NOT currently + * attached to any hooks. However, it is called upon by {@link autoClearPostCache()}. + */ + public function autoClearPostsPageCache() + { + $counter = 0; // Initialize. + + if (!is_null($done = &$this->cacheKey('autoClearPostsPageCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_posts_page_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $show_on_front = get_option('show_on_front'); + $page_for_posts = get_option('page_for_posts'); + + if (!in_array($show_on_front, ['posts', 'page'], true)) { + return $counter; // Nothing we can do in this case. + } + if ($show_on_front === 'page' && !$page_for_posts) { + return $counter; // Nothing we can do. + } + if ($show_on_front === 'posts') { + $posts_page = home_url('/'); + } elseif ($show_on_front === 'page') { + $posts_page = get_permalink($page_for_posts); + } + if (empty($posts_page)) { + return $counter; // Nothing we can do. + } + $regex = $this->buildHostCachePathRegex($posts_page); + $counter += $this->clearFilesFromHostCacheDir($regex); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for the designated "Posts Page"; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + $counter += $this->autoClearXmlFeedsCache('blog'); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpJetpackUtils.php b/src/includes/traits/Plugin/WcpJetpackUtils.php new file mode 100644 index 0000000..17433cb --- /dev/null +++ b/src/includes/traits/Plugin/WcpJetpackUtils.php @@ -0,0 +1,30 @@ +cacheKey('autoClearCacheOnJetpackCustomCss', $args))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (empty($args['is_preview']) && class_exists('\\Jetpack')) { + $counter += $this->autoClearCache(); + } + } +} diff --git a/src/includes/traits/Plugin/WcpOpcacheUtils.php b/src/includes/traits/Plugin/WcpOpcacheUtils.php new file mode 100644 index 0000000..8a2cb80 --- /dev/null +++ b/src/includes/traits/Plugin/WcpOpcacheUtils.php @@ -0,0 +1,77 @@ +options['cache_clear_opcache_enable']) { + return $counter; // Not enabled at this time. + } + if (!$this->functionIsPossible('opcache_reset')) { + return $counter; // Not possible. + } + if (!($status = $this->sysOpcacheStatus())) { + return $counter; // Not possible. + } + if (empty($status->opcache_enabled)) { + return $counter; // Not necessary. + } + if (empty($status->opcache_statistics->num_cached_keys)) { + return $counter; // Not possible. + } + if ($files) { // Specific files? + foreach ($files as $_file) { + $counter += (int) opcache_invalidate($_file, true); + } // unset($_file); // Housekeeping. + } elseif (opcache_reset()) { // True if a reset occurs. + $counter += $status->opcache_statistics->num_cached_keys; + } + return $counter; + } + + /** + * Clear (i.e., reset) OPCache. + * + * @since 151002 Adding OPCache support. + * + * @param bool $manually True if clearing is done manually. + * @param bool $maybe Defaults to a true value. + * + * @return int Total keys cleared. + */ + public function clearOpcache($manually = false, $maybe = true) + { + if (!is_multisite() || is_main_site() || current_user_can($this->network_cap)) { + return $this->wipeOpcache($manually, $maybe); + } + return 0; // Not applicable. + } + + /** + * Clear AC class file from Opcache (by force). + * + * @since 151215 Adding OPCache support. + * + * @return int Total keys cleared. + */ + public function clearAcDropinFromOpcacheByForce() + { + return $this->wipeOpcache(false, false, [WP_CONTENT_DIR.'/advanced-cache.php']); + } +} diff --git a/src/includes/traits/Plugin/WcpPluginUtils.php b/src/includes/traits/Plugin/WcpPluginUtils.php new file mode 100644 index 0000000..4617f91 --- /dev/null +++ b/src/includes/traits/Plugin/WcpPluginUtils.php @@ -0,0 +1,28 @@ +applyWpFilters(GLOBAL_NS.'_auto_clear_on_plugin_activation_deactivation', true)) { + return 0; // Nothing to do here. + } + return $this->{($network_wide ? 'autoWipeCache' : 'autoClearCache')}(); + } +} diff --git a/src/includes/traits/Plugin/WcpPostTypeUtils.php b/src/includes/traits/Plugin/WcpPostTypeUtils.php new file mode 100644 index 0000000..0f47e72 --- /dev/null +++ b/src/includes/traits/Plugin/WcpPostTypeUtils.php @@ -0,0 +1,71 @@ +cacheKey('autoClearCustomPostTypeArchiveCache', $post_id))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_custom_post_type_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + if (!($post_type = get_post_type($post_id))) { + return $counter; // Nothing to do. + } + if (!($all_custom_post_types = get_post_types(['_builtin' => false]))) { + return $counter; // No custom post types. + } + if (!in_array($post_type, array_keys($all_custom_post_types), true)) { + return $counter; // This is NOT a custom post type. + } + if (!($custom_post_type = get_post_type_object($post_type))) { + return $counter; // Unable to retrieve post type. + } + if (empty($custom_post_type->labels->name) || !($custom_post_type_name = $custom_post_type->labels->name)) { + $custom_post_type_name = __('Untitled', 'comet-cache'); + } + if (!($custom_post_type_archive_link = get_post_type_archive_link($post_type))) { + return $counter; // Nothing to do; no link to work from in this case. + } + $regex = $this->buildHostCachePathRegex($custom_post_type_archive_link); + $counter += $this->clearFilesFromHostCacheDir($regex); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for Custom Post Type: %2$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter)), esc_html($custom_post_type_name)), ['combinable' => true]); + } + $counter += $this->autoClearXmlFeedsCache('custom-post-type', $post_id); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpPostUtils.php b/src/includes/traits/Plugin/WcpPostUtils.php new file mode 100644 index 0000000..f343b87 --- /dev/null +++ b/src/includes/traits/Plugin/WcpPostUtils.php @@ -0,0 +1,190 @@ +cacheKey('autoClearPostCache_allow'))) { + if ($allow === false) { // Disallow? + $allow = true; // Reset flag. + return $counter; + } + } + if (!($post_id = (integer) $post_id)) { + return $counter; // Nothing to do. + } + if (!is_null($done = &$this->cacheKey('autoClearPostCache', [$post_id, $force]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + if (!($post_type = get_post_type($post_id))) { + return $counter; // Nothing to do. + } + $post_statuses = $this->postStatuses(); + $unpublished_post_statuses = array_diff($post_statuses, ['publish']); + $is_bbpress_post_type = in_array($post_type, $this->bbPressPostTypes(), true); + + if (!empty($this->pre_post_update_post_permalink[$post_id])) { + $permalink = $this->pre_post_update_post_permalink[$post_id]; + $this->pre_post_update_post_permalink[$post_id] = ''; // Reset; only used for post status transitions. + } elseif (!($permalink = get_permalink($post_id))) { + return $counter; // Nothing we can do. + } + if (!($post_status = get_post_status($post_id))) { + return $counter; // Nothing to do. + } + if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish']) + && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts') + && strpos(wp_get_referer(), '/post-new.php') !== false + ) { + $post_status = 'publish'; // A new post being published now. + } + if (in_array($post_status, ['inherit', 'auto-draft'], true)) { + return $counter; // Nothing to do. Note: `inherit` = revision. + } + if (in_array($post_status, ['draft', 'pending', 'future', 'trash'], true) && !$force) { + return $counter; // Nothing to do; i.e., NOT forcing in this case. + } + if (($post_type_obj = get_post_type_object($post_type)) && !empty($post_type_obj->labels->singular_name)) { + $post_type_singular_name = $post_type_obj->labels->singular_name; // Singular name for the post type. + } else { + $post_type_singular_name = __('Post', 'comet-cache'); // Default value. + } + $regex = $this->buildHostCachePathRegex($permalink); + $counter += $this->clearFilesFromHostCacheDir($regex); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for %2$s ID: %3$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter)), esc_html($post_type_singular_name), esc_html($post_id)), ['combinable' => true]); + } + $counter += $this->autoClearXmlFeedsCache('blog'); + $counter += $this->autoClearXmlFeedsCache('post-terms', $post_id); + $counter += $this->autoClearXmlFeedsCache('post-authors', $post_id); + + $counter += $this->autoClearXmlSitemapsCache(); + $counter += $this->autoClearHomePageCache(); + $counter += $this->autoClearPostsPageCache(); + $counter += $this->autoClearPostTermsCache($post_id, $force); + $counter += $this->autoClearCustomPostTypeArchiveCache($post_id); + + + if ($post_type !== 'page' && ($parent_post_id = wp_get_post_parent_id($post_id))) { + // Recursion: i.e., nested post types like bbPress forums/topic/replies. + $counter += $this->autoClearPostCache($parent_post_id, $force); + } + return $counter; + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for autoClearPostCache() + */ + public function auto_clear_post_cache() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'autoClearPostCache'], func_get_args()); + } + + /** + * Handles post status transitioning. + * + * @attaches-to `pre_post_update` hook. + * + * @since 150422 Rewrite. + * + * @param int $post_id Post ID. + * @param array $data Array of unslashed post data. + * + * @throws \Exception If a clear failure occurs. + * + * @return int Total files cleared by this routine (if any). + * + * @note This is also called upon by other routines which listen for + * events that are indirectly associated with a post ID. + */ + public function autoClearPostCacheTransition($post_id, $data) + { + $counter = 0; // Initialize. + + $old_status = (string) get_post_status($post_id); + $new_status = (string) $data['post_status']; + /* + * When a post has a status of `pending` or `draft`, the `get_permalink()` function + * does not return a friendly permalink and therefore `autoClearPostCache()` will + * have no way of building a path to the cache file that should be cleared as part of + * this post status transition. To get around this, we temporarily store the permalink + * in $this->pre_post_update_post_permalink for `autoClearPostCache()` to use. + * + * See also: + */ + if (in_array($new_status, ['pending', 'draft'], true)) { + $this->pre_post_update_post_permalink[$post_id] = get_permalink($post_id); + } + // Begin post status transition sub-routine now. + + if (!is_null($done = &$this->cacheKey('autoClearPostCacheTransition', [$old_status, $new_status, $post_id]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if ($new_status === $old_status) { + return $counter; // Nothing to do. + } + if (!($post_type = get_post_type($post_id))) { + return $counter; // Nothing to do. + } + $post_statuses = $this->postStatuses(); + $unpublished_post_statuses = array_diff($post_statuses, ['publish']); + $is_bbpress_post_type = in_array($post_type, $this->bbPressPostTypes(), true); + + if ($is_bbpress_post_type) { + if (in_array($old_status, ['publish', 'private', 'closed', 'spam', 'hidden'], true)) { + if (in_array($new_status, $unpublished_post_statuses, true)) { + $counter += $this->autoClearPostCache($post_id, true); + } + } + } elseif (in_array($old_status, ['publish', 'private'], true)) { + if (in_array($new_status, $unpublished_post_statuses, true)) { + $counter += $this->autoClearPostCache($post_id, true); + } + } + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpSettingUtils.php b/src/includes/traits/Plugin/WcpSettingUtils.php new file mode 100644 index 0000000..27c0518 --- /dev/null +++ b/src/includes/traits/Plugin/WcpSettingUtils.php @@ -0,0 +1,37 @@ +cacheKey('autoClearCacheOnSettingChanges', [$pagenow, $settings_updated]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if ($pagenow === 'options-general.php' && $settings_updated) { + $counter += $this->autoClearCache(); + } elseif ($pagenow === 'options-reading.php' && $settings_updated) { + $counter += $this->autoClearCache(); + } elseif ($pagenow === 'options-discussion.php' && $settings_updated) { + $counter += $this->autoClearCache(); + } elseif ($pagenow === 'options-permalink.php' && $settings_updated) { + $counter += $this->autoClearCache(); + } + } +} diff --git a/src/includes/traits/Plugin/WcpSitemapUtils.php b/src/includes/traits/Plugin/WcpSitemapUtils.php new file mode 100644 index 0000000..94c5ee5 --- /dev/null +++ b/src/includes/traits/Plugin/WcpSitemapUtils.php @@ -0,0 +1,52 @@ +cacheKey('autoClearXmlSitemapsCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_xml_sitemaps_enable']) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_xml_sitemap_patterns']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + if (!($regex_frags = $this->buildHostCachePathRegexFragsFromWcUris($this->options['cache_clear_xml_sitemap_patterns'], ''))) { + return $counter; // There are no patterns to look for. + } + $regex = $this->buildHostCachePathRegex('', '\/'.$regex_frags.'\.'); + $counter += $this->clearFilesFromHostCacheDir($regex); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for XML sitemaps; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpTermUtils.php b/src/includes/traits/Plugin/WcpTermUtils.php new file mode 100644 index 0000000..84ee1dc --- /dev/null +++ b/src/includes/traits/Plugin/WcpTermUtils.php @@ -0,0 +1,144 @@ +cacheKey('autoClearPostTermsCache', [$post_id, $force]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return $counter; // Nothing to do. + } + if (!$this->options['cache_clear_term_category_enable'] && !$this->options['cache_clear_term_post_tag_enable'] && !$this->options['cache_clear_term_other_enable']) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $post_status = get_post_status($post_id); // Cache this. + + if ($post_status === 'draft' && isset($GLOBALS['pagenow'], $_POST['publish']) + && is_admin() && $GLOBALS['pagenow'] === 'post.php' && current_user_can('publish_posts') + && strpos(wp_get_referer(), '/post-new.php') !== false + ) { + $post_status = 'publish'; // A new post being published now. + } + if (in_array($post_status, ['inherit', 'auto-draft'], true)) { + return $counter; // Nothing to do. Note: `inherit` = revision. + } + if (in_array($post_status, ['draft', 'pending', 'future'], true) && !$force) { + return $counter; // Nothing to do; i.e., NOT forcing in this case. + } + /* + * Build an array of available taxonomies for this post (as taxonomy objects). + */ + $taxonomies = get_object_taxonomies(get_post($post_id), 'objects'); + + if (!is_array($taxonomies)) { + return $counter; // Nothing to do. + } + /* + * Build an array of terms associated with this post for each taxonomy. + * Also save taxonomy label information for Dashboard messaging later. + */ + $terms = []; + $taxonomy_labels = []; + + foreach ($taxonomies as $_taxonomy) { + if (// Check if this is a taxonomy/term that we should clear. + ($_taxonomy->name === 'category' && !$this->options['cache_clear_term_category_enable']) + || ($_taxonomy->name === 'post_tag' && !$this->options['cache_clear_term_post_tag_enable']) + || ($_taxonomy->name !== 'category' && $_taxonomy->name !== 'post_tag' && !$this->options['cache_clear_term_other_enable']) + ) { + continue; // Continue; nothing to do for this taxonomy. + } + if (is_array($_terms = wp_get_post_terms($post_id, $_taxonomy->name))) { + $terms = array_merge($terms, $_terms); + if (empty($_taxonomy->labels->singular_name) || $_taxonomy->labels->singular_name === '') { + $taxonomy_labels[$_taxonomy->name] = $_taxonomy->name; + } else { + $taxonomy_labels[$_taxonomy->name] = $_taxonomy->labels->singular_name; + } + } + } + unset($_taxonomy, $_terms); + + if (empty($terms)) { + return $counter; // Nothing to do. + } + /* + * Build an array of terms with term names, + * permalinks, and associated taxonomy labels. + */ + $terms_to_clear = []; + $_i = 0; + + foreach ($terms as $_term) { + if (($_link = get_term_link($_term))) { + $terms_to_clear[$_i]['permalink'] = $_link; + $terms_to_clear[$_i]['term_name'] = $_term->name; + if (!empty($taxonomy_labels[$_term->taxonomy])) { + $terms_to_clear[$_i]['taxonomy_label'] = $taxonomy_labels[$_term->taxonomy]; + } else { + $terms_to_clear[$_i]['taxonomy_label'] = $_term->taxonomy; + } + } + ++$_i; // Array index counter. + } + unset($_term, $_link, $_i); + + if (empty($terms_to_clear)) { + return $counter; // Nothing to do. + } + foreach ($terms_to_clear as $_term) { + $_term_regex = $this->buildHostCachePathRegex($_term['permalink']); + $_term_counter = $this->clearFilesFromHostCacheDir($_term_regex); + $counter += $_term_counter; // Add to overall counter. + + if ($_term_counter && $enqueued_notices < 100 && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Found %1$s in the cache for %2$s: %3$s; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($_term_counter)), esc_html($_term['taxonomy_label']), esc_html($_term['term_name'])), ['combinable' => true]); + ++$enqueued_notices; // Increment enqueued notices counter. + } + } + unset($_term, $_term_regex, $_term_counter); // Housekeeping. + + $counter += $this->autoClearXmlFeedsCache('post-terms', $post_id); + + return $counter; + } +} diff --git a/src/includes/traits/Plugin/WcpUpdaterUtils.php b/src/includes/traits/Plugin/WcpUpdaterUtils.php new file mode 100644 index 0000000..4e5b2b9 --- /dev/null +++ b/src/includes/traits/Plugin/WcpUpdaterUtils.php @@ -0,0 +1,103 @@ +autoClearCache(); + } + break; // Break switch. + + case 'theme': // Theme upgrade. + + $current_active_theme = wp_get_theme(); + $current_active_theme_parent = $current_active_theme->parent(); + $multi_theme_update = $single_theme_update = false; + $upgrading_active_parent_theme = $upgrading_active_theme = false; + + if (!empty($data['bulk']) && !empty($data['themes']) && is_array($data['themes'])) { + $multi_theme_update = true; + } elseif (!empty($data['theme']) && is_string($data['theme'])) { + $single_theme_update = true; + } + if ($multi_theme_update) { + foreach ($data['themes'] as $_theme) { + if (!$_theme || !is_string($_theme) || !($_theme_obj = wp_get_theme($_theme))) { + continue; // Unable to acquire theme object instance. + } + if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) { + $upgrading_active_parent_theme = true; + break; // Got what we needed here. + } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) { + $upgrading_active_theme = true; + break; // Got what we needed here. + } + } + unset($_theme, $_theme_obj); // Housekeeping. + } elseif ($single_theme_update && ($_theme_obj = wp_get_theme($data['theme']))) { + if ($current_active_theme_parent && $current_active_theme_parent->get_stylesheet() === $_theme_obj->get_stylesheet()) { + $upgrading_active_parent_theme = true; + } elseif ($current_active_theme->get_stylesheet() === $_theme_obj->get_stylesheet()) { + $upgrading_active_theme = true; + } + } + unset($_theme_obj); // Housekeeping. + + if ($upgrading_active_theme || $upgrading_active_parent_theme) { + $counter += $this->autoClearCache(); + } + break; // Break switch. + + case 'core': // Core upgrade. + default: // Or any other sort of upgrade. + $counter += $this->autoClearCache(); + break; // Break switch. + } + } +} diff --git a/src/includes/traits/Plugin/WcpUtils.php b/src/includes/traits/Plugin/WcpUtils.php new file mode 100644 index 0000000..2bdf1a4 --- /dev/null +++ b/src/includes/traits/Plugin/WcpUtils.php @@ -0,0 +1,383 @@ +disableAutoWipeCacheRoutines()) { + return $counter; // Nothing to do. + } + @set_time_limit(1800); // @TODO Display a warning. + + if (is_dir($cache_dir = $this->cacheDir())) { + $regex = $this->assembleCachePathRegex('', '.+'); + $counter += $this->wipeFilesFromCacheDir($regex); + } + + + + + + return $counter; + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for autoClearUserCache() + */ + public function wipe_cache() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'wipeCache'], func_get_args()); + } + + /** + * Clears cache files (current blog). + * + * @since 150422 Rewrite. + * + * @param bool $manually TRUE if clearing is done manually. + * + * @throws \Exception If a clearing failure occurs. + * + * @return int Total files cleared by this routine. + */ + public function clearCache($manually = false) + { + $counter = 0; // Initialize. + + if (!$manually && $this->disableAutoClearCacheRoutines()) { + return $counter; // Nothing to do. + } + @set_time_limit(1800); // @TODO Display a warning. + + if (is_dir($cache_dir = $this->cacheDir())) { + $regex = $this->buildHostCachePathRegex('', '.+'); + $counter += $this->clearFilesFromHostCacheDir($regex); + } + + + + + + return $counter; + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for clearCache() + */ + public function clear_cache() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'clearCache'], func_get_args()); + } + + /** + * Purges expired cache files (current blog). + * + * @since 150422 Rewrite. + * + * @param bool $manually TRUE if purging is done manually. + * + * @throws \Exception If a purge failure occurs. + * + * @return int Total files purged by this routine. + */ + public function purgeCache($manually = false) + { + $counter = 0; // Initialize. + + if (!$manually && $this->disableAutoPurgeCacheRoutines()) { + return $counter; // Nothing to do. + } + @set_time_limit(1800); // @TODO Display a warning. + + if (is_dir($cache_dir = $this->cacheDir())) { + $regex = $this->buildHostCachePathRegex('', '.+'); + $counter += $this->purgeFilesFromHostCacheDir($regex); + } + + return $counter; + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for purgeCache() + */ + public function purge_cache() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'purgeCache'], func_get_args()); + } + + /** + * Wurges (purges) all expired cache files; like wipe, but expired files only. + * + * @since 151002 Look at entire cache directory. + * + * @param bool $manually TRUE if wurging is done manually. + * + * @throws \Exception If a wurge failure occurs. + * + * @return int Total files wurged by this routine. + */ + public function wurgeCache($manually = false) + { + $counter = 0; // Initialize. + + if (!$manually && $this->disableAutoPurgeCacheRoutines()) { + return $counter; // Nothing to do. + } + @set_time_limit(1800); // @TODO Display a warning. + + if (is_dir($cache_dir = $this->cacheDir())) { + $regex = $this->assembleCachePathRegex('', '.+'); + $counter += $this->wurgeFilesFromCacheDir($regex); + } + + return $counter; + } + + /** + * Automatically wipes out all cache files. + * + * @attaches-to Nothing at this time. + * + * @since 150422 Rewrite. + * + * @return int Total files wiped by this routine (if any). + * + * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. + * This is called upon whenever options are saved and/or restored though. + */ + public function autoWipeCache() + { + $counter = 0; // Initialize. + + if (!is_null($done = &$this->cacheKey('autoWipeCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if ($this->disableAutoWipeCacheRoutines()) { + return $counter; // Nothing to do. + } + $counter += $this->wipeCache(); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Detected significant changes that require a full wipe of the cache. Found %1$s in the cache; auto-wiping.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + return $counter; + } + + /** + * Automatically clears all cache files (current host). + * + * @attaches-to `switch_theme` hook. + * + * @attaches-to `wp_create_nav_menu` hook. + * @attaches-to `wp_update_nav_menu` hook. + * @attaches-to `wp_delete_nav_menu` hook. + * + * @attaches-to `create_term` hook. + * @attaches-to `edit_terms` hook. + * @attaches-to `delete_term` hook. + * + * @attaches-to `add_link` hook. + * @attaches-to `edit_link` hook. + * @attaches-to `delete_link` hook. + * + * @since 150422 Rewrite. + * + * @return int Total files cleared by this routine (if any). + * + * @note This is also called upon during plugin activation. + */ + public function autoClearCache() + { + $counter = 0; // Initialize. + + if (!is_null($done = &$this->cacheKey('autoClearCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if ($this->disableAutoClearCacheRoutines()) { + return $counter; // Nothing to do. + } + $counter += $this->clearCache(); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Detected important site changes that affect the entire cache. Found %1$s in the cache for this site; auto-clearing.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + return $counter; + } + + /** + * Automatically purges all cache files (current host). + * + * @attaches-to Nothing at this time. + * + * @since 151002 While working on directory stats. + * + * @return int Total files purged by this routine. + * + * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. + */ + public function autoPurgeCache() + { + $counter = 0; // Initialize. + + if (!is_null($done = &$this->cacheKey('autoPurgeCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if ($this->disableAutoPurgeCacheRoutines()) { + return $counter; // Nothing to do. + } + $counter += $this->purgeCache(); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Detected important site changes. Found %1$s in the cache for this site that were expired; auto-purging.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + return $counter; + } + + /** + * Automatically wurges all cache files. + * + * @attaches-to Nothing at this time. + * + * @since 151002 While working on directory stats. + * + * @return int Total files wurged by this routine. + * + * @note Unlike many of the other `auto_` methods, this one is NOT currently attached to any hooks. + */ + public function autoWurgeCache() + { + $counter = 0; // Initialize. + + if (!is_null($done = &$this->cacheKey('autoWurgeCache'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } + if ($this->disableAutoPurgeCacheRoutines()) { + return $counter; // Nothing to do. + } + $counter += $this->wurgeCache(); + + if ($counter && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueNotice(sprintf(__('Detected important site changes. Found %1$s in the cache that were expired; auto-purging.', 'comet-cache'), esc_html($this->i18nFiles($counter))), ['combinable' => true]); + } + return $counter; + } + + /** + * Allows a site owner to disable the automatic cache wiping routines. + * + * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_wipe_cache_routines` to return TRUE, + * in which case this method returns TRUE, otherwise it returns FALSE. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. + */ + public function disableAutoWipeCacheRoutines() + { + $is_disabled = (boolean) $this->applyWpFilters(GLOBAL_NS.'_disable_auto_wipe_cache_routines', false); + + if ($is_disabled && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueMainNotice( + ''. + sprintf(__('%1$s: detected significant changes that would normally trigger cache wiping routines. However, cache wiping routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME)) + ); + } + return $is_disabled; + } + + /** + * Allows a site owner to disable the automatic cache clearing routines. + * + * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_clear_cache_routines` to return TRUE, + * in which case this method returns TRUE, otherwise it returns FALSE. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. + */ + public function disableAutoClearCacheRoutines() + { + $is_disabled = (boolean) $this->applyWpFilters(GLOBAL_NS.'_disable_auto_clear_cache_routines', false); + + if ($is_disabled && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueMainNotice( + ''. + sprintf(__('%1$s: detected important site changes that would normally trigger cache clearing routines. However, cache clearing routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME)) + ); + } + return $is_disabled; + } + + /** + * Allows a site owner to disable the automatic cache purging routines. + * + * This is done by filtering `'.__GLOBAL_NS__.'_disable_auto_purge_cache_routines` to return TRUE, + * in which case this method returns TRUE, otherwise it returns FALSE. + * + * @since 151002 While working on directory stats. + * + * @return bool `TRUE` if disabled; and this also creates a dashboard notice in some cases. + */ + public function disableAutoPurgeCacheRoutines() + { + $is_disabled = (boolean) $this->applyWpFilters(GLOBAL_NS.'_disable_auto_purge_cache_routines', false); + + if ($is_disabled && is_admin() && (!IS_PRO || $this->options['change_notifications_enable'])) { + $this->enqueueMainNotice( + ''. + sprintf(__('%1$s: detected important site changes that would normally trigger cache purging routines. However, cache purging routines have been disabled by a site administrator. [?]', 'comet-cache'), esc_html(NAME)) + ); + } + return $is_disabled; + } +} diff --git a/src/includes/traits/Plugin/WcpWooCommerceUtils.php b/src/includes/traits/Plugin/WcpWooCommerceUtils.php new file mode 100644 index 0000000..78db58d --- /dev/null +++ b/src/includes/traits/Plugin/WcpWooCommerceUtils.php @@ -0,0 +1,30 @@ +cacheKey('autoClearPostCacheOnWooCommerceSetStock'))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (class_exists('\\WooCommerce')) { + $counter += $this->autoClearPostCache($product->id); + } + } +} diff --git a/src/includes/traits/Shared/.build.php b/src/includes/traits/Shared/.build.php new file mode 100644 index 0000000..38ee54d --- /dev/null +++ b/src/includes/traits/Shared/.build.php @@ -0,0 +1,28 @@ +isAdvancedCache()) { + return null; // Not possible. + } + if (($blog_id = (integer) $blog_id) < 0) { + $blog_id = (integer) get_current_site()->blog_id; + } + if (!$blog_id) { + $blog_id = (integer) get_current_blog_id(); + } + if (!$blog_id || $blog_id < 0) { + return null; // Not possible. + } + $details = get_blog_details($blog_id); + + return is_object($details) ? $details : null; + } +} diff --git a/src/includes/traits/Shared/CacheDirUtils.php b/src/includes/traits/Shared/CacheDirUtils.php new file mode 100644 index 0000000..2e75528 --- /dev/null +++ b/src/includes/traits/Shared/CacheDirUtils.php @@ -0,0 +1,651 @@ +isAdvancedCache()) { + $cache_dir = defined('COMET_CACHE_DIR') ? COMET_CACHE_DIR : ''; + } elseif (!empty($this->cache_sub_dir)) { + $cache_dir = $this->wpContentBaseDirTo($this->cache_sub_dir); + } + if (empty($cache_dir)) { + throw new \Exception(__('Unable to determine cache directory location.', 'comet-cache')); + } + return rtrim($cache_dir, '/').($rel_path ? '/'.ltrim($rel_path) : ''); + } + + /** + * Wipe files from the cache directory (for all hosts/blogs); + * i.e., those that match a specific regex pattern. + * + * @since 151002 While working on directory stats. + * + * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}. + * + * @return int Total files wiped by this routine. + */ + public function wipeFilesFromCacheDir($regex) + { + return $this->deleteFilesFromCacheDir($regex); + } + + /** + * Clear files from the cache directory (for the current host); + * i.e., those that match a specific regex pattern. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}. + * + * @return int Total files cleared by this routine (if any). + */ + public function clearFilesFromHostCacheDir($regex) + { + return $this->deleteFilesFromHostCacheDir($regex); + } + + /** + * Wurge (purge) files from the cache directory (for all hosts/blogs); + * i.e., those that match a specific regex pattern. + * + * @since 151002 While working on directory stats. + * + * @param string $regex A regex pattern; see {@link deleteFilesFromCacheDir()}. + * + * @return int Total files wurged by this routine. + */ + public function wurgeFilesFromCacheDir($regex) + { + return $this->deleteFilesFromCacheDir($regex, true); + } + + /** + * Purge files from the cache directory (for the current host); + * i.e., those that match a specific regex pattern. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $regex A regex pattern; see {@link deleteFilesFromHostCacheDir()}. + * + * @return int Total files purged by this routine (if any). + */ + public function purgeFilesFromHostCacheDir($regex) + { + return $this->deleteFilesFromHostCacheDir($regex, true); + } + + /** + * Delete files from the cache directory (for all hosts/blogs); + * i.e., those that match a specific regex pattern. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $regex A `/[regex pattern]/`; relative to the cache directory. + * e.g. `/^http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` + * + * Or, this can also be a full/absolute regex pattern against an absolute path; + * provided that it always starts with `/^`; including the full absolute cache/host directory path. + * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` + * @param bool $check_max_age Check max age? i.e., use purge behavior? + * + * @throws \Exception If unable to delete a file for any reason. + * @return int Total files deleted by this routine (if any). + * + * + * @TODO Optimize this for multisite networks w/ a LOT of child blogs. + * @TODO Optimize this for extremely large sites. A LOT of files here could slow things down. + * This class member is currently used in wiping and purging for a network. So there is the potential for a LOT of files in a single scan. + * See also: + */ + public function deleteFilesFromCacheDir($regex, $check_max_age = false) + { + $counter = 0; // Initialize. + + if (!($regex = (string) $regex)) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $cache_dir = $this->nDirSeps($cache_dir); + + if ($check_max_age && $this->isAdvancedCache()) { + throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache')); + } + if ($check_max_age && !($max_age = strtotime('-'.$this->options['cache_max_age']))) { + return $counter; // Invalid cache expiration time. + } + /* ------- Begin lock state... ----------- */ + + $cache_lock = $this->cacheLock(); // Lock cache writes. + + clearstatcache(); // Clear stat cache to be sure we have a fresh start below. + + $cache_dir_tmp = $this->addTmpSuffix($cache_dir); // Temporary directory. + + $cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory. + $cache_dir_tmp_regex = '\\/'.ltrim($cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`. + $cache_dir_tmp_regex = $this->strIreplaceOnce(preg_quote($cache_dir.'/', '/'), '', $cache_dir_tmp_regex); + + $cache_dir_tmp_regex = ltrim($cache_dir_tmp_regex, '^\\/'); + if (strpos($cache_dir_tmp_regex, '(?:\/') === 0 || strpos($cache_dir_tmp_regex, '(\/') === 0) { + $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp, '/').$cache_dir_tmp_regex; + } else { + $cache_dir_tmp_regex = '/^'.preg_quote($cache_dir_tmp.'/', '/').$cache_dir_tmp_regex; + } + # if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND); + // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it. + + if (!rename($cache_dir, $cache_dir_tmp)) { + throw new \Exception(sprintf(__('Unable to delete files. Rename failure on directory: `%1$s`.', 'comet-cache'), $cache_dir)); + } + foreach (($_dir_regex_iteration = $this->dirRegexIteration($cache_dir_tmp, $cache_dir_tmp_regex)) as $_resource) { + $_resource_type = $_resource->getType(); + $_sub_path_name = $_resource->getSubpathname(); + $_path_name = $_resource->getPathname(); + + if ($_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) { + continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc. + // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes. + } + switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. + + case 'link': // Symbolic links; i.e., 404 errors. + + if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) { + if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) { + if ($_lstat['mtime'] >= $max_age) { + break; // Break switch. + } + } + } + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each link we delete. + + break; // Break switch handler. + + case 'file': // Regular files; i.e., not symlinks. + + if ($check_max_age && !empty($max_age)) { + if ($_resource->getMTime() >= $max_age) { + break; // Break switch. + } + } + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each file we delete. + + break; // Break switch handler. + + case 'dir': // A regular directory; i.e., not a symlink. + + if ($regex !== '/^.+/i') { + break; // Not deleting everything. + } + if ($check_max_age && !empty($max_age)) { + break; // Not deleting everything. + } + if (!rmdir($_path_name)) { + $this->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); + } + # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here. + + break; // Break switch handler. + + default: // Something else that is totally unexpected here. + $this->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); + } + } + unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping. + + if (!rename($cache_dir_tmp, $cache_dir)) { + $this->tryErasingAllFilesDirsIn($cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $cache_dir_tmp)); + } + /* ------- End lock state... ------------- */ + + $this->cacheUnlock($cache_lock); // Release. + + return $counter; + } + + /** + * Delete files from the cache directory (for the current host); + * i.e., those that match a specific regex pattern. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $regex A `/[regex pattern]/`; relative to the host cache directory. + * e.g. `/^my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` + * + * Or, this can also be a full/absolute regex pattern against an absolute path; + * provided that it always starts with `/^`; including the full absolute cache/host directory path. + * e.g. `/^\/cache\/dir\/http\/example\.com\/my\-slug(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])/` + * @param bool $check_max_age Check max age? i.e., use purge behavior? + * @param bool $___considering_domain_mapping For internal use only. + * @param bool $___consider_domain_mapping_host_token For internal use only. + * @param bool $___consider_domain_mapping_host_base_dir_tokens For internal use only. + * + * @throws \Exception If unable to delete a file for any reason. + * @return int Total files deleted by this routine (if any). + * + */ + public function deleteFilesFromHostCacheDir( + $regex, + $check_max_age = false, + $___considering_domain_mapping = false, + $___consider_domain_mapping_host_token = null, + $___consider_domain_mapping_host_base_dir_tokens = null + ) { + $counter = 0; // Initialize. + + if (!($regex = (string) $regex)) { + return $counter; // Nothing to do. + } + if (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + $cache_dir = $this->nDirSeps($cache_dir); // Normalize. + $host_token = $current_host_token = $this->hostToken(); + $host_base_dir_tokens = $current_host_base_dir_tokens = $this->hostBaseDirTokens(); + + if ($___considering_domain_mapping && isset($___consider_domain_mapping_host_token, $___consider_domain_mapping_host_base_dir_tokens)) { + $host_token = (string) $___consider_domain_mapping_host_token; + $host_base_dir_tokens = (string) $___consider_domain_mapping_host_base_dir_tokens; + } + if (!$host_token) { // Must have a host in the sub-routine below. + throw new \Exception(__('Invalid argument; host token empty!', 'comet-cache')); + } + if ($check_max_age && $this->isAdvancedCache()) { + throw new \Exception(__('Invalid argument; isAdvancedCache!', 'comet-cache')); + } + if ($check_max_age && !($max_age = strtotime('-'.$this->options['cache_max_age']))) { + return $counter; // Invalid cache expiration time. + } + /* ------- Begin lock state... ----------- */ + + $cache_lock = $this->cacheLock(); // Lock cache writes. + + clearstatcache(); // Clear stat cache to be sure we have a fresh start below. + + foreach (['http', 'https'] as $_host_scheme) { + $_host_url = $_host_scheme.'://'.$host_token.$host_base_dir_tokens; + $_host_cache_path_flags = $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT; + $_host_cache_path = $this->buildCachePath($_host_url, '', '', $_host_cache_path_flags); + $_host_cache_dir = $this->nDirSeps($cache_dir.'/'.$_host_cache_path); // Normalize. + + if (!$_host_cache_dir || !is_dir($_host_cache_dir)) { + // On a multisite install this may have a cache sub-directory. + // e.g., `http/example-com[[-base]-child1][[/base]/child1]` instead of `http/example-com`. + continue; // Nothing to do. + } + $_host_cache_dir_tmp = $this->addTmpSuffix($_host_cache_dir); // Temporary directory. + + $_host_cache_dir_tmp_regex = $regex; // Initialize host-specific regex pattern for the tmp directory. + $_host_cache_dir_tmp_regex = '\\/'.ltrim($_host_cache_dir_tmp_regex, '^\\/'); // Make sure it begins with an escaped `/`. + $_host_cache_dir_tmp_regex = $this->strIreplaceOnce(preg_quote($_host_cache_path.'/', '/'), '', $_host_cache_dir_tmp_regex); + $_host_cache_dir_tmp_regex = $this->strIreplaceOnce(preg_quote($_host_cache_dir.'/', '/'), '', $_host_cache_dir_tmp_regex); + + $_host_cache_dir_tmp_regex = ltrim($_host_cache_dir_tmp_regex, '^\\/'); + if (strpos($_host_cache_dir_tmp_regex, '(?:\/') === 0 || strpos($_host_cache_dir_tmp_regex, '(\/') === 0) { + $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp, '/').$_host_cache_dir_tmp_regex; + } else { + $_host_cache_dir_tmp_regex = '/^'.preg_quote($_host_cache_dir_tmp.'/', '/').$_host_cache_dir_tmp_regex; + } + #if(WP_DEBUG) file_put_contents(WP_CONTENT_DIR.'/'.strtolower(SHORT_NAME).'-debug.log', print_r($regex, TRUE)."\n".print_r($_host_cache_dir_tmp_regex, TRUE)."\n\n", FILE_APPEND); + // Uncomment the above line to debug regex pattern matching used by this routine; and others that call upon it. + + if (!rename($_host_cache_dir, $_host_cache_dir_tmp)) { + throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir)); + } + foreach (($_dir_regex_iteration = $this->dirRegexIteration($_host_cache_dir_tmp, $_host_cache_dir_tmp_regex)) as $_resource) { + $_resource_type = $_resource->getType(); + $_sub_path_name = $_resource->getSubpathname(); + $_path_name = $_resource->getPathname(); + + if ($_host_cache_dir === $cache_dir && $_resource_type !== 'dir' && strpos($_sub_path_name, '/') === false) { + continue; // Don't delete links/files in the immediate directory; e.g. `[SHORT_NAME]-advanced-cache` or `.htaccess`, etc. + // Actual `http|https/...` cache links/files are nested. Links/files in the immediate directory are for other purposes. + } + switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. + + case 'link': // Symbolic links; i.e., 404 errors. + + if ($check_max_age && !empty($max_age) && is_file($_resource->getLinkTarget())) { + if (($_lstat = lstat($_path_name)) && !empty($_lstat['mtime'])) { + if ($_lstat['mtime'] >= $max_age) { + break; // Break switch. + } + } + } + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each link we delete. + + break; // Break switch handler. + + case 'file': // Regular files; i.e., not symlinks. + + if ($check_max_age && !empty($max_age)) { + if ($_resource->getMTime() >= $max_age) { + break; // Break switch handler. + } + } + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each file we delete. + + break; // Break switch handler. + + case 'dir': // A regular directory; i.e., not a symlink. + + if ($regex !== '/^.+/i') { + break; // Not deleting everything. + } + if ($check_max_age && !empty($max_age)) { + break; // Not deleting everything. + } + if (!rmdir($_path_name)) { + $this->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); + } + # $counter++; // Increment counter for each directory we delete. ~ NO don't do that here. + + break; // Break switch handler. + + default: // Something else that is totally unexpected here. + $this->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); + } + } + unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name, $_lstat); // Housekeeping. + + if (!rename($_host_cache_dir_tmp, $_host_cache_dir)) { + $this->tryErasingAllFilesDirsIn($_host_cache_dir_tmp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete files. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $_host_cache_dir_tmp)); + } + } + unset($_host_scheme, $_host_url, $_host_cache_path_flags, $_host_cache_path, $_host_cache_dir, $_host_cache_dir_tmp, $_host_cache_dir_tmp_regex); + + /* ------- End lock state... ------------- */ + + $this->cacheUnlock($cache_lock); // Release. + + /* ------- Include domain mapping variations also. ------- */ + + if (!$___considering_domain_mapping && is_multisite() && $this->canConsiderDomainMapping()) { + $domain_mapping_variations = []; // Initialize array of domain variations. + + if (($_host_token_for_blog = $this->hostTokenForBlog())) { + $_host_base_dir_tokens_for_blog = $this->hostBaseDirTokensForBlog(); + $domain_mapping_variations[] = ['host_token' => $_host_token_for_blog, 'host_base_dir_tokens' => $_host_base_dir_tokens_for_blog]; + } // The original blog host; i.e., without domain mapping. + unset($_host_token_for_blog, $_host_base_dir_tokens_for_blog); // Housekeeping. + + foreach ($this->domainMappingBlogDomains() as $_domain_mapping_blog_domain) { + if (($_domain_host_token_for_blog = $this->hostTokenForBlog(false, true, $_domain_mapping_blog_domain))) { + $_domain_host_base_dir_tokens_for_blog = $this->hostBaseDirTokensForBlog(false, true); // This is only a formality. + $domain_mapping_variations[] = ['host_token' => $_domain_host_token_for_blog, 'host_base_dir_tokens' => $_domain_host_base_dir_tokens_for_blog]; + } + } // This includes all of the domain mappings configured for the current blog ID. + unset($_domain_mapping_blog_domain, $_domain_host_token_for_blog, $_domain_host_base_dir_tokens_for_blog); // Housekeeping. + + foreach ($domain_mapping_variations as $_domain_mapping_variation) { + if ($_domain_mapping_variation['host_token'] === $current_host_token && $_domain_mapping_variation['host_base_dir_tokens'] === $current_host_base_dir_tokens) { + continue; // Exclude current tokens. They were already iterated above. + } + $counter += $this->deleteFilesFromHostCacheDir($regex, $check_max_age, true, $_domain_mapping_variation['host_token'], $_domain_mapping_variation['host_base_dir_tokens']); + } + unset($_domain_mapping_variation); // Housekeeping. + } + return $counter; + } + + /** + * Delete all files/dirs from a directory (for all schemes/hosts); + * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $dir The directory from which to delete files/dirs. + * + * SECURITY: This directory MUST be located inside the `/wp-content/` directory. + * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. + * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. + * @param bool $delete_dir_too Delete parent? i.e., delete the `$dir` itself also? + * + * @throws \Exception If unable to delete a file/directory for any reason. + * @return int Total files/directories deleted by this routine (if any). + * + */ + public function deleteAllFilesDirsIn($dir, $delete_dir_too = false) + { + $counter = 0; // Initialize. + + if (!($dir = trim((string) $dir)) || !is_dir($dir)) { + return $counter; // Nothing to do. + } + $dir = $this->nDirSeps($dir); + $dir_temp = $this->addTmpSuffix($dir); + $wp_content_dir = $this->nDirSeps(WP_CONTENT_DIR); + $wp_content_dir_regex = preg_quote($wp_content_dir, '/'); + + if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) { + return $counter; // Security flag; do nothing in this case. + } + if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) { + return $counter; // Security flag; do nothing in this case. + } + /* ------- Begin lock state... ----------- */ + + $cache_lock = $this->cacheLock(); // Lock cache writes. + + clearstatcache(); // Clear stat cache to be sure we have a fresh start below. + + if (!rename($dir, $dir_temp)) { + throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir)); + } + foreach (($_dir_regex_iteration = $this->dirRegexIteration($dir_temp, '/.+/')) as $_resource) { + $_resource_type = $_resource->getType(); + $_sub_path_name = $_resource->getSubpathname(); + $_path_name = $_resource->getPathname(); + + switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. + + case 'link': // Symbolic links; i.e., 404 errors. + + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete symlink: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each link we delete. + + break; // Break switch handler. + + case 'file': // Regular files; i.e., not symlinks. + + if (!unlink($_path_name)) { + $this->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete file: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each file we delete. + + break; // Break switch handler. + + case 'dir': // A regular directory; i.e., not a symlink. + + if (!rmdir($_path_name)) { + $this->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete dir: `%1$s`.', 'comet-cache'), $_path_name)); + } + # ++$counter; // Increment counter for each directory we delete. ~ NO don't do that here. + + break; // Break switch handler. + + default: // Something else that is totally unexpected here. + $this->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); + } + } + unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping. + + if (!rename($dir_temp, $dir)) { + $this->tryErasingAllFilesDirsIn($dir_temp, true); // Cleanup if possible. + throw new \Exception(sprintf(__('Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`.', 'comet-cache'), $dir_temp)); + } + if ($delete_dir_too) { + if (!rmdir($dir)) { + throw new \Exception(sprintf(__('Unable to delete directory: `%1$s`.', 'comet-cache'), $dir)); + } + ++$counter; // Increment counter for each directory we delete. + } + /* ------- End lock state... ------------- */ + + $this->cacheUnlock($cache_lock); // Release. + + return $counter; + } + + /** + * Erase all files/dirs from a directory (for all schemes/hosts); + * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. + * + * WARNING: This does NO LOCKING and NO ATOMIC deletions. + * + * @since 150821 Improving recovery under stress. + * + * @param string $dir The directory from which to erase files/dirs. + * + * SECURITY: This directory MUST be located inside the `/wp-content/` directory. + * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. + * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. + * @param bool $erase_dir_too Erase parent? i.e., erase the `$dir` itself also? + * + * @throws \Exception If unable to erase a file/directory for any reason. + * @return int Total files/directories erased by this routine (if any). + * + */ + public function eraseAllFilesDirsIn($dir, $erase_dir_too = false) + { + $counter = 0; // Initialize. + + if (!($dir = trim((string) $dir)) || !is_dir($dir)) { + return $counter; // Nothing to do. + } + $dir = $this->nDirSeps($dir); + $wp_content_dir = $this->nDirSeps(WP_CONTENT_DIR); + $wp_content_dir_regex = preg_quote($wp_content_dir, '/'); + + if (!preg_match('/^'.$wp_content_dir_regex.'\/[^\/]+/i', $dir)) { + return $counter; // Security flag; do nothing in this case. + } + if (preg_match('/^'.$wp_content_dir_regex.'\/(?:mu\-plugins|themes|plugins)(?:\/|$)/i', $dir)) { + return $counter; // Security flag; do nothing in this case. + } + clearstatcache(); // Clear stat cache to be sure we have a fresh start below. + + foreach (($_dir_regex_iteration = $this->dirRegexIteration($dir, '/.+/')) as $_resource) { + $_resource_type = $_resource->getType(); + $_sub_path_name = $_resource->getSubpathname(); + $_path_name = $_resource->getPathname(); + + switch ($_resource_type) {// Based on type; i.e., `link`, `file`, `dir`. + + case 'link': // Symbolic links; i.e., 404 errors. + + if (!unlink($_path_name)) { + throw new \Exception(sprintf(__('Unable to erase symlink: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each link we erase. + + break; // Break switch handler. + + case 'file': // Regular files; i.e., not symlinks. + + if (!unlink($_path_name)) { + throw new \Exception(sprintf(__('Unable to erase file: `%1$s`.', 'comet-cache'), $_path_name)); + } + ++$counter; // Increment counter for each file we erase. + + break; // Break switch handler. + + case 'dir': // A regular directory; i.e., not a symlink. + + if (!rmdir($_path_name)) { + throw new \Exception(sprintf(__('Unable to erase dir: `%1$s`.', 'comet-cache'), $_path_name)); + } + # ++$counter; // Increment counter for each directory we erase. ~ NO don't do that here. + + break; // Break switch handler. + + default: // Something else that is totally unexpected here. + throw new \Exception(sprintf(__('Unexpected resource type: `%1$s`.', 'comet-cache'), $_resource_type)); + } + } + unset($_dir_regex_iteration, $_resource, $_resource_type, $_sub_path_name, $_path_name); // Housekeeping. + + if ($erase_dir_too) { + if (!rmdir($dir)) { + throw new \Exception(sprintf(__('Unable to erase directory: `%1$s`.', 'comet-cache'), $dir)); + } + ++$counter; // Increment counter for each directory we erase. + } + return $counter; + } + + /** + * Try to erase all files/dirs from a directory (for all schemes/hosts); + * including `[SHORT_NAME]-` prefixed files; or anything else for that matter. + * + * WARNING: This does NO LOCKING and NO ATOMIC deletions. + * + * @since 150821 Improving recovery under stress. + * + * @param string $dir The directory from which to erase files/dirs. + * + * SECURITY: This directory MUST be located inside the `/wp-content/` directory. + * Also, it MUST be a sub-directory of `/wp-content/`, NOT the directory itself. + * Also, it cannot be: `mu-plugins`, `themes`, or `plugins`. + * @param bool $erase_dir_too Erase parent? i.e., erase the `$dir` itself also? + * + * @return int Total files/directories erased by this routine (if any). + */ + public function tryErasingAllFilesDirsIn($dir, $erase_dir_too = false) + { + $counter = 0; // Initialize counter. + try { + $counter += $this->eraseAllFilesDirsIn($dir, $erase_dir_too); + } catch (\Exception $exception) { + // Fail softly. + } + return $counter; + } +} diff --git a/src/includes/traits/Shared/CacheLockUtils.php b/src/includes/traits/Shared/CacheLockUtils.php new file mode 100644 index 0000000..33bbc0e --- /dev/null +++ b/src/includes/traits/Shared/CacheLockUtils.php @@ -0,0 +1,87 @@ +applyWpFilters(GLOBAL_NS.'\\share::disable_cache_locking', false) + || $this->applyWpFilters(GLOBAL_NS.'_disable_cache_locking', false) + ) { + return false; // Disabled cache locking. + } + if (!($wp_config_file = $this->findWpConfigFile())) { + throw new \Exception(__('Unable to find the wp-config.php file.', 'comet-cache')); + } + $lock_type = 'flock'; // Default lock type. + $lock_type = $this->applyWpFilters(GLOBAL_NS.'\\share::cache_lock_lock_type', $lock_type); + $lock_type = $this->applyWpFilters(GLOBAL_NS.'_cache_lock_type', $lock_type); + + if (!in_array($lock_type, ['flock', 'sem'], true)) { + $lock_type = 'flock'; // Default lock type. + } + if ($lock_type === 'sem' && $this->functionIsPossible('sem_get')) { + if (($ipc_key = ftok($wp_config_file, 'w'))) { + if (($resource = sem_get($ipc_key, 1)) && sem_acquire($resource)) { + return ['type' => 'sem', 'resource' => $resource]; + } + } + } + if (!($tmp_dir = $this->getTmpDir())) { + throw new \Exception(__('No writable tmp directory.', 'comet-cache')); + } + $inode_key = fileinode($wp_config_file); + $mutex = $tmp_dir.'/'.SLUG_TD.'-'.$inode_key.'.lock'; + + if (!($resource = fopen($mutex, 'wb')) || !flock($resource, LOCK_EX)) { + throw new \Exception(__('Unable to obtain an exclusive lock.', 'comet-cache')); + } + + @chmod($mutex, 0666); // See https://git.io/v2WAt + + return ['type' => 'flock', 'resource' => $resource]; + } + + /** + * Release an exclusive lock on the cache directory. + * + * @since 150422 Rewrite. Updated 151002 to remove the `array` typecast. + * + * @param array|mixed $lock Type & resource. + */ + public function cacheUnlock($lock) + { + if (!is_array($lock)) { + return; // Not possible. + // Or, they disabled cache locking. + } + if (empty($lock['type']) || empty($lock['resource'])) { + return; // Not possible. + } + if (!is_resource($lock['resource'])) { + return; // Not possible. + } + if ($lock['type'] === 'sem') { + sem_release($lock['resource']); + } elseif ($lock['type'] === 'flock') { + flock($lock['resource'], LOCK_UN); + fclose($lock['resource']); + } + } +} diff --git a/src/includes/traits/Shared/CachePathUtils.php b/src/includes/traits/Shared/CachePathUtils.php new file mode 100644 index 0000000..0e2a4c1 --- /dev/null +++ b/src/includes/traits/Shared/CachePathUtils.php @@ -0,0 +1,358 @@ +cachePathRegexDefaultSuffixFrag(); + } + return (string) $regex_suffix_frag; + } + + /** + * Default cache-path suffix frag (regex). + * + * @since 151220 Enhancing translation support. + * + * @return string Default cache-path suffix frag (regex). + * + * @TODO Use conditional to detect the AMP plugin (e.g., `isAmpInstalled()`) to avoid edge cases with the `|\/amp` regex here + */ + public function cachePathRegexDefaultSuffixFrag() + { + if ($this->isPlugin() && !empty($GLOBALS['wp_rewrite'])) { + $pagination_base = $GLOBALS['wp_rewrite']->pagination_base; + $comments_pagination_base = $GLOBALS['wp_rewrite']->comments_pagination_base; + return '(?:\/index|\/amp)?(?:\.|\/(?:'.preg_quote($pagination_base, '/').'\/[0-9]+|'.preg_quote($comments_pagination_base, '/').'\-[0-9]+)[.\/])'; + } else { + return '(?:\/index|\/amp)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])'; + } + } + + /** + * Converts a URL into a `cache/path` based on input `$flags`. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $url The input URL to convert. + * @param string $with_user_token Optional user token (if applicable). + * @param string $with_version_salt Optional version salt (if applicable). + * @param int $flags Optional flags. A bitmask via `$this::CACHE_PATH_*` constants. + * + * @return string The resulting `cache/path` based on the input `$url` & `$flags`. + */ + public function buildCachePath($url, $with_user_token = '', $with_version_salt = '', $flags = self::CACHE_PATH_DEFAULT) + { + # Force parameter types. + + $url = trim((string) $url); + $with_user_token = trim((string) $with_user_token); + $with_version_salt = trim((string) $with_version_salt); + + # Initialize variables. + + $is_multisite = is_multisite(); + $can_consider_domain_mapping = $is_multisite && $this->canConsiderDomainMapping(); + $cache_path = ''; // Initialize cache path being built here. + + # Deal w/ domain mapping considerations. + + if ($flags & $this::CACHE_PATH_CONSIDER_DOMAIN_MAPPING && $is_multisite && $can_consider_domain_mapping) { + if ($flags & $this::CACHE_PATH_REVERSE_DOMAIN_MAPPING) { + $url = $this->domainMappingReverseUrlFilter($url); + } else { + $url = $this->domainMappingUrlFilter($url); + } + } + # Validate the URL we have now. + + if (!$url || !($url_parts = $this->parseUrl($url))) { + return $cache_path = ''; // Not possible. + } + if (empty($url_parts['scheme']) || $url_parts['scheme'] === '//') { + return $cache_path = ''; // Not possible. + } + if (empty($url_parts['host'])) { + return $cache_path = ''; // Not possible. + } + # Initialize additional variables; based on the parsed URL. + + $is_url_domain_mapped = $is_multisite && $can_consider_domain_mapping && $this->domainMappingBlogId($url); + $host_base_dir_tokens = $this->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/'); + + $is_a_multisite_base_dir = $is_multisite && $host_base_dir_tokens && $host_base_dir_tokens !== '/' // Check? + && stripos(!empty($url_parts['path']) ? rtrim($url_parts['path'], '/').'/' : '/', $host_base_dir_tokens) === 0; + + $is_a_multisite_base_dir_root = $is_multisite && $is_a_multisite_base_dir // Save time by using the previous check here. + && strcasecmp(trim($host_base_dir_tokens, '/'), trim(!empty($url_parts['path']) ? $url_parts['path'] : '/', '/')) === 0; + + # Build and return the cache path. + + if (!($flags & $this::CACHE_PATH_NO_SCHEME)) { + $cache_path .= $url_parts['scheme'].'/'; + } + if (!($flags & $this::CACHE_PATH_NO_HOST)) { + $cache_path .= $url_parts['host'].'/'; + + // Put multisite sub-roots into a host directory of their own. + // e.g., `example-com[[-base]-child1]` instead of `example-com`. + if ($is_a_multisite_base_dir && $host_base_dir_tokens && $host_base_dir_tokens !== '/') { + $host_base_dir_suffix = preg_replace('/[^a-z0-9.]/i', '-', rtrim($host_base_dir_tokens, '/')); + $cache_path = rtrim($cache_path, '/').$host_base_dir_suffix.'/'; + } + } + if (!($flags & $this::CACHE_PATH_NO_PATH)) { + if (isset($url_parts['path'][201])) { + $_path_tmp = '/'; // Initialize tmp path. + foreach (explode('/', $url_parts['path']) as $_path_component) { + if (!isset($_path_component[0])) { + continue; // Empty. + } + if (isset($_path_component[201])) { + $_path_component = 'lpc-'.sha1($_path_component); + } + $_path_tmp .= $_path_component.'/'; + } + $url_parts['path'] = $_path_tmp; // Shorter components. + unset($_path_component, $_path_tmp); // Housekeeping. + + if (isset($url_parts['path'][2001])) { + $url_parts['path'] = '/lp-'.sha1($url_parts['path']).'/'; + } + } // Now add the path and check for a possible root `index/` suffix. + if (!empty($url_parts['path']) && strlen($url_parts['path'] = trim($url_parts['path'], '\\/'." \t\n\r\0\x0B"))) { + $cache_path .= $url_parts['path'].'/'; // Add the path as it exists. + + if (!($flags & $this::CACHE_PATH_NO_PATH_INDEX) && $is_multisite && $is_a_multisite_base_dir_root) { + // We should build an `index/` when this ends with a multisite [[/base]/child1] root. + // e.g., `http/example-com[[-base]-child1][[/base]/child1]` is a root directory. + $cache_path .= 'index/'; // Use an index suffix. + } + } elseif (!($flags & $this::CACHE_PATH_NO_PATH_INDEX)) { + $cache_path .= 'index/'; + } + } + if ($this->isExtensionLoaded('mbstring') && mb_check_encoding($cache_path, 'UTF-8')) { + $cache_path = mb_strtolower($cache_path, 'UTF-8'); + } + $cache_path = str_replace('.', '-', strtolower($cache_path)); + + if (!($flags & $this::CACHE_PATH_NO_QUV)) { + if (!($flags & $this::CACHE_PATH_NO_QUERY)) { + if (isset($url_parts['query']) && $url_parts['query'] !== '') { + $cache_path = rtrim($cache_path, '/').'.q/'.md5($url_parts['query']).'/'; + } + } + if (!($flags & $this::CACHE_PATH_NO_USER)) { + if ($with_user_token !== '') { + $cache_path = rtrim($cache_path, '/').'.u/'.str_replace(['/', '\\'], '-', $with_user_token).'/'; + } + } + if (!($flags & $this::CACHE_PATH_NO_VSALT)) { + if ($with_version_salt !== '') { + $cache_path = rtrim($cache_path, '/').'.v/'.str_replace(['/', '\\'], '-', $with_version_salt).'/'; + } + } + } + $cache_path = trim(preg_replace(['/\/+/', '/\.+/'], ['/', '.'], $cache_path), '/'); + + if ($flags & $this::CACHE_PATH_ALLOW_WD_REGEX) { + $cache_path = preg_replace('/[^a-z0-9\/.*\^$]/i', '-', $cache_path); + } elseif ($flags & $this::CACHE_PATH_ALLOW_WILDCARDS) { + $cache_path = preg_replace('/[^a-z0-9\/.*]/i', '-', $cache_path); + } else { + $cache_path = preg_replace('/[^a-z0-9\/.]/i', '-', $cache_path); + } + if (!($flags & $this::CACHE_PATH_NO_EXT)) { + $cache_path .= '.html'; + } + return $cache_path; + } + + /** + * Regex pattern for a call to `deleteFilesFromCacheDir()`. + * + * @since 151114 Updated to support an arbitrary URL instead of a regex frag. + * + * @param string $url The input URL to convert. This CAN be left empty when necessary. + * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. + * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. + * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. + * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. + * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. + * See also: {@link $this::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. + * + * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. + */ + public function buildCachePathRegex($url, $regex_suffix_frag = self::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) + { + $url = trim((string) $url); + $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); + $cache_path_regex = ''; // Initialize regex. + + if ($url) { + $flags = $this::CACHE_PATH_NO_SCHEME // Scheme added below. + | $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT; + $cache_path = $this->buildCachePath($url, '', '', $flags); // Without the scheme. + $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.preg_quote($cache_path, '/') : ''; + } + return '/^'.$cache_path_regex.$regex_suffix_frag.'/i'; + } + + /** + * Regex pattern for a call to `deleteFilesFromHostCacheDir()`. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $url The input URL to convert. This CAN be left empty when necessary. + * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. + * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. + * @param string $regex_suffix_frag Regex fragment to come after the relative cache/path regex frag. + * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. + * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. + * See also: {@link $this::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. + * + * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`. + */ + public function buildHostCachePathRegex($url, $regex_suffix_frag = self::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) + { + $url = trim((string) $url); + $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); + $abs_relative_cache_path_regex = ''; // Initialize regex. + $is_url_domain_mapped = false; // Initialize. + + if ($url) { + if (is_multisite() && $this->canConsiderDomainMapping()) { + // Shortest possible URI; i.e., consider domain mapping. + $url = $this->domainMappingUrlFilter($url); + $is_url_domain_mapped = $url && $this->domainMappingBlogId($url); + } + if ($url && ($url_parts = $this->parseUrl($url)) && !empty($url_parts['host'])) { + $flags = $this::CACHE_PATH_NO_SCHEME | $this::CACHE_PATH_NO_HOST + | $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT; + + $host_base_dir_tokens = $this->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/'); + $host_url = rtrim('http://'.$url_parts['host'].$host_base_dir_tokens, '/'); + $host_cache_path = $this->buildCachePath($host_url, '', '', $flags); + + $cache_path = $this->buildCachePath($url, '', '', $flags); + $relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $cache_path); + + $abs_relative_cache_path = isset($relative_cache_path[0]) ? '/'.$relative_cache_path : ''; + $abs_relative_cache_path_regex = isset($abs_relative_cache_path[0]) ? preg_quote($abs_relative_cache_path, '/') : ''; + } + } + return '/^'.$abs_relative_cache_path_regex.$regex_suffix_frag.'/i'; + } + + /** + * Regex pattern for a call to `deleteFilesFromCacheDir()`. + * + * @since 151114 Improving watered-down regex syntax. + * + * @param string $url The input URL to convert. This CAN be left empty when necessary. + * This may also contain watered-down regex; i.e., `*^$` characters are OK here. + * However, `^$` are discarded, as they are unnecessary in this context. + * + * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. + * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. + * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. + * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. + * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. + * See also: {@link $this::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. + * + * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. + */ + public function buildCachePathRegexFromWcUrl($url, $regex_suffix_frag = self::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) + { + $url = trim((string) $url, '^$'); + $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); + $cache_path_regex = ''; // Initialize regex. + + if ($url) { // After `^$` trimming above. + $flags = $this::CACHE_PATH_ALLOW_WILDCARDS | $this::CACHE_PATH_NO_SCHEME + | $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT; + $cache_path = $this->buildCachePath($url, '', '', $flags); // Without the scheme. + $cache_path_regex = isset($cache_path[0]) ? '\/https?\/'.$this->wdRegexToActualRegexFrag($cache_path) : ''; + } + return '/^'.$cache_path_regex.$regex_suffix_frag.'/i'; + } + + /** + * Regex pattern for a call to `deleteFilesFromHostCacheDir()`. + * + * @since 150422 Rewrite. Updated 151002 w/ multisite compat. improvements. + * + * @param string $uris A line-delimited list of URIs. These may contain `*^$` also. + * However, `^$` are discarded, as they are unnecessary in this context. + * @param string $regex_suffix_frag Regex fragment to come after each relative cache/path. + * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. + * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. + * See also: {@link $this::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. + * + * @return string Regex pattern for a call to `deleteFilesFromHostCacheDir()`. + */ + public function buildHostCachePathRegexFragsFromWcUris($uris, $regex_suffix_frag = self::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) + { + $uris = trim((string) $uris); + $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); + + $flags = $this::CACHE_PATH_ALLOW_WILDCARDS | $this::CACHE_PATH_NO_SCHEME | $this::CACHE_PATH_NO_HOST + | $this::CACHE_PATH_NO_PATH_INDEX | $this::CACHE_PATH_NO_QUV | $this::CACHE_PATH_NO_EXT; + + $host = 'doesnt-matter.foo.bar'; + $host_url = rtrim('http://'.$host, '/'); + $host_cache_path = $this->buildCachePath($host_url, '', '', $flags); + $uri_patterns = array_unique(preg_split('/['."\r\n".']+/', $uris, -1, PREG_SPLIT_NO_EMPTY)); + + foreach ($uri_patterns as $_key => &$_uri_pattern) { + if (($_uri_pattern = trim($_uri_pattern, '^$'))) { + $_cache_path = $this->buildCachePath($host_url.'/'.trim($_uri_pattern, '/'), '', '', $flags); + $_relative_cache_path = preg_replace('/^'.preg_quote($host_cache_path, '/').'(?:\/|$)/i', '', $_cache_path); + $_uri_pattern = $this->wdRegexToActualRegexFrag($_relative_cache_path); + } + if (!$_uri_pattern) { + unset($uri_patterns[$_key]); // Remove it. + } + } + unset($_key, $_uri_pattern, $_cache_path, $_relative_cache_path); // Housekeeping. + + return $uri_patterns ? '(?:'.implode('|', array_unique($uri_patterns)).')'.$regex_suffix_frag : ''; + } + + /** + * Regex pattern for a call to `deleteFilesFromCacheDir()`. + * + * @since 151114 Moving this low-level routine into a method of a different name. + * + * @param string $regex_frag A regex fragment. This CAN be left empty when necessary. + * If empty, the final regex pattern will be `/^'.$regex_suffix_frag.'/i`. + * If empty, it's a good idea to start `$regex_suffix_frag` with `.*?`. + * @param string $regex_suffix_frag Regex fragment to come after the `$regex_frag`. + * Defaults to: `(?:\/index)?(?:\.|\/(?:page\/[0-9]+|comment\-page\-[0-9]+)[.\/])`. + * Note: this should NOT have delimiters; i.e. do NOT start or end with `/`. + * See also: {@link $this::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG}. + * + * @return string Regex pattern for a call to `deleteFilesFromCacheDir()`. + */ + public function assembleCachePathRegex($regex_frag, $regex_suffix_frag = self::CACHE_PATH_REGEX_DEFAULT_SUFFIX_FRAG) + { + $regex_frag = (string) $regex_frag; + $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); + + return '/^'.$regex_frag.$regex_suffix_frag.'/i'; + } +} diff --git a/src/includes/traits/Shared/ConditionalUtils.php b/src/includes/traits/Shared/ConditionalUtils.php new file mode 100644 index 0000000..e8303d5 --- /dev/null +++ b/src/includes/traits/Shared/ConditionalUtils.php @@ -0,0 +1,380 @@ + 'die', + 'echo' => 'echo', + 'empty' => 'empty', + 'exit' => 'exit', + 'eval' => 'eval', + 'include' => 'include', + 'include_once' => 'include_once', + 'isset' => 'isset', + 'list' => 'list', + 'require' => 'require', + 'require_once' => 'require_once', + 'return' => 'return', + 'print' => 'print', + 'unset' => 'unset', + '__halt_compiler' => '__halt_compiler', + ]; + + /** + * Is AdvancedCache class? + * + * @since 150821 Improving multisite compat. + * + * @return bool `TRUE` if this is the AdvancedCache class. + */ + public function isAdvancedCache() + { + return $this instanceof Classes\AdvancedCache; + } + + /** + * Is Plugin class? + * + * @since 150821 Improving multisite compat. + * + * @return bool `TRUE` if this is the Plugin class. + */ + public function isPlugin() + { + return $this instanceof Classes\Plugin; + } + + /** + * Is the current request method `POST`, `PUT` or `DELETE`? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if current request method is `POST`, `PUT` or `DELETE`. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isPostPutDeleteRequest() + { + if (!is_null($is = &$this->staticKey('isPostPutDeleteRequest'))) { + return $is; // Already cached this. + } + if (!empty($_POST)) { + return $is = true; + } + if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { + if (in_array(strtoupper($_SERVER['REQUEST_METHOD']), ['POST', 'PUT', 'DELETE'], true)) { + return $is = true; + } + } + return $is = false; + } + + /** + * Does the current request include an uncacheable query string? + * + * @since 151002 Improving Nginx support. + * + * @return bool True if request includes an uncacheable query string. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function requestContainsUncacheableQueryVars() + { + if (!is_null($is = &$this->staticKey('requestContainsUncacheableQueryVars'))) { + return $is; // Already cached this. + } + if (!empty($_GET) || !empty($_SERVER['QUERY_STRING'])) { + $_get_count = !empty($_GET) ? count($_GET) : 0; + $is_abc_only = $_get_count === 1 && isset($_GET[strtolower(SHORT_NAME).'ABC']); + $is_nginx_q_only = $_get_count === 1 && isset($_GET['q']) && $this->isNginx(); + $is_ac_get_var_true = isset($_GET[strtolower(SHORT_NAME).'AC']) && filter_var($_GET[strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN); + + if (!$is_abc_only && !$is_nginx_q_only && !$is_ac_get_var_true) { + return $is = true; + } + } + return $is = false; + } + + /** + * Is the current request method is uncacheable? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if current request method is uncacheable. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isUncacheableRequestMethod() + { + if (!is_null($is = &$this->staticKey('isUncacheableRequestMethod'))) { + return $is; // Already cached this. + } + if (!empty($_POST)) { + return $is = true; + } + if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { + if (!in_array(strtoupper($_SERVER['REQUEST_METHOD']), ['GET'], true)) { + return $is = true; + } + } + return $is = false; + } + + /** + * Should the current user should be considered a logged-in user? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if current user should be considered a logged-in user. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isLikeUserLoggedIn() + { + if (!is_null($is = &$this->staticKey('isLikeUserLoggedIn'))) { + return $is; // Already cached this. + } + if (defined('SID') && SID) { + return $is = true; // Session ID. + } + if (empty($_COOKIE)) { + return $is = false; // No cookies. + } + $regex_logged_in_cookies = '/^'; // Initialize. + + if (defined('LOGGED_IN_COOKIE') && LOGGED_IN_COOKIE) { + $regex_logged_in_cookies .= preg_quote(LOGGED_IN_COOKIE, '/'); + } else { // Use the default hard-coded cookie prefix. + $regex_logged_in_cookies .= 'wordpress_logged_in_'; + } + $regex_logged_in_cookies .= '|comment_author_'; + $regex_logged_in_cookies .= '|wp[_\-]postpass_'; + + $regex_logged_in_cookies .= '/'; // Close regex. + + foreach ($_COOKIE as $_key => $_value) { + if ($_value && preg_match($regex_logged_in_cookies, $_key)) { + return $is = true; // Like a logged-in user. + } + } + unset($_key, $_value); // Housekeeping. + + return $is = false; + } + + /** + * Are we in a LOCALHOST environment? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if we are in a LOCALHOST environment. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isLocalhost() + { + if (!is_null($is = &$this->staticKey('isLocalhost'))) { + return $is; // Already cached this. + } + if (defined('LOCALHOST')) { + return $is = (boolean) LOCALHOST; + } + if (preg_match('/\b(?:localhost|127\.0\.0\.1)\b/i', $this->hostToken())) { + return $is = true; + } + return $is = false; + } + + + + /** + * Is the current request for a feed? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if the current request is for a feed. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isFeed() + { + if (!is_null($is = &$this->staticKey('isFeed'))) { + return $is; // Already cached this. + } + if (isset($_REQUEST['feed'])) { + return $is = true; + } + if (!empty($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI'])) { + if (preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI'])) { + return $is = true; + } + } + return $is = false; + } + + /** + * Is a document/string an HTML/XML doc; or no? + * + * @since 150422 Rewrite. + * + * @param string $doc Input string/document to check. + * + * @return bool True if `$doc` is an HTML/XML doc type. + */ + public function isHtmlXmlDoc($doc) + { + $doc = trim((string) $doc); + $doc_hash = sha1($doc); + + if (!is_null($is = &$this->staticKey('isHtmlXmlDoc', $doc_hash))) { + return $is; // Already cached this. + } + if (stripos($doc, '') !== false) { + return $is = true; + } + if (stripos($doc, 'staticKey('hasACacheableContentType'))) { + return $is; // Already cached this. + } + foreach ($this->headersList() as $_key => $_header) { + if (stripos($_header, 'Content-Type:') === 0) { + $content_type = $_header; // Last one. + } + } + unset($_key, $_header); // Housekeeping. + + if (isset($content_type[0]) && stripos($content_type, 'html') === false + && stripos($content_type, 'xml') === false && stripos($content_type, GLOBAL_NS) === false + ) { + return $is = false; // Do NOT cache data sent by scripts serving other MIME types. + } + return $is = true; + } + + /** + * Does the current request have a cacheable HTTP status code? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if the current request has a cacheable HTTP status code. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + * + * @warning Do NOT call upon this method until the end of a script execution. + */ + public function hasACacheableStatus() + { + if (!is_null($is = &$this->staticKey('hasACacheableStatus'))) { + return $is; // Already cached this. + } + if (($http_status = (string) $this->httpStatus()) && $http_status[0] !== '2' && $http_status !== '404') { + return $is = false; // A non-2xx & non-404 status code. + } + foreach ($this->headersList() as $_key => $_header) { + if (preg_match('/^(?:Retry\-After\:\s+(?P.+)|Status\:\s+(?P[0-9]+)|HTTP\/[0-9]+(?:\.[0-9]+)?\s+(?P[0-9]+))/i', $_header, $_m)) { + if (!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404') + || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404') + ) { + return $is = false; // Not a cacheable status. + } + } + } + unset($_key, $_header); // Housekeeping. + + return $is = true; + } + + /** + * Checks if a PHP extension is loaded up. + * + * @since 150422 Rewrite. + * + * @param string $extension A PHP extension slug (i.e. extension name). + * + * @return bool `TRUE` if the extension is loaded. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isExtensionLoaded($extension) + { + $extension = (string) $extension; + + if (!is_null($is = &$this->staticKey('isExtensionLoaded', $extension))) { + return $is; // Already cached this. + } + return $is = (boolean) extension_loaded($extension); + } + + /** + * Is a particular function possible in every way? + * + * @since 150422 Rewrite. + * + * @param string $function A PHP function (or user function) to check. + * + * @return string `TRUE` if the function is possible. + * + * @note This checks (among other things) if the function exists and that it's callable. + * It also checks the currently configured `disable_functions` and `suhosin.executor.func.blacklist`. + */ + public function functionIsPossible($function) + { + $function = (string) $function; + + if (!is_null($is = &$this->staticKey('functionIsPossible', $function))) { + return $is; // Already cached this. + } + if (is_null($disabled_functions = &$this->staticKey('functionIsPossible_disabled_functions'))) { + $disabled_functions = []; // Initialize disabled/blacklisted functions. + + if (($disable_functions = trim(ini_get('disable_functions')))) { + $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($disable_functions), -1, PREG_SPLIT_NO_EMPTY)); + } + if (($blacklist_functions = trim(ini_get('suhosin.executor.func.blacklist')))) { + $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', strtolower($blacklist_functions), -1, PREG_SPLIT_NO_EMPTY)); + } + if (filter_var(ini_get('suhosin.executor.disable_eval'), FILTER_VALIDATE_BOOLEAN)) { + $disabled_functions = array_merge($disabled_functions, ['eval']); + } + } + if (!function_exists($function) || !is_callable($function)) { + if (!in_array($function, $this->php_constructs, true)) { // A language construct + return $is = false; // Not possible. + } + } + if ($disabled_functions && in_array(strtolower($function), $disabled_functions, true)) { + return $is = false; // Not possible. + } + return $is = true; + } +} diff --git a/src/includes/traits/Shared/DomainMappingUtils.php b/src/includes/traits/Shared/DomainMappingUtils.php new file mode 100644 index 0000000..4634d3a --- /dev/null +++ b/src/includes/traits/Shared/DomainMappingUtils.php @@ -0,0 +1,277 @@ +staticKey('canConsiderDomainMapping'))) { + return $can; // Already cached this. + } + if (!$this->isAdvancedCache() && is_multisite() && $this->hostBaseToken() === '/' + && defined('SUNRISE_LOADED') && SUNRISE_LOADED && !empty($GLOBALS['dm_domain']) + ) { + return $can = true; // Can consider. + } + return $can = false; // Cannot consider. + } + + /** + * Domain mapping? + * + * @since 150821 Improving multisite compat. + * + * @return int Domain mapping ID; else `0` (false). + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isDomainMapping() + { + if (!is_null($is = &$this->staticKey('isDomainMapping'))) { + return $is; // Already cached this. + } + if (!$this->isAdvancedCache() && is_multisite() && $this->canConsiderDomainMapping() + && defined('DOMAIN_MAPPING') && DOMAIN_MAPPING && !empty($GLOBALS['domain_mapping_id']) + ) { + return $is = (integer) $GLOBALS['domain_mapping_id']; // Blog ID. + } + return $is = 0; // Not domain mapping. + } + + /** + * Filters a URL in order to apply domain mapping. + * + * @since 150821 Improving multisite compat. + * + * @param string $url The input URL to filter. + * + * @return string The filtered URL; else the original URL. + * + * @note The return value of this function is NOT cached, but inner portions are. + */ + public function domainMappingUrlFilter($url) + { + $original_url = (string) $url; // Preserve. + $url = trim((string) $url); + + if (!is_multisite() || !$this->canConsiderDomainMapping()) { + return $original_url; // Not possible. + } + if (!$url || !($url_parts = $this->parseUrl($url))) { + return $original_url; // Not possible. + } + if (empty($url_parts['host'])) { + return $original_url; // Not possible. + } + $blog_domain = strtolower($url_parts['host']); // In the unfiltered URL. + $blog_path = $this->hostDirToken(false, false, !empty($url_parts['path']) ? $url_parts['path'] : '/'); + + if (!($blog_id = (integer) get_blog_id_from_url($blog_domain, $blog_path))) { + return $original_url; // Not possible. + } + if (!($domain = $this->domainMappingBlogDomain($blog_id)) || $domain === $blog_domain) { + return $original_url; // Not applicable. + } + $url_parts['host'] = $domain; // Filter the URL now. + if (!empty($url_parts['path']) && $url_parts['path'] !== '/') { + if (($host_base_dir_tokens = trim($this->hostBaseDirTokens(false, false, $url_parts['path']), '/'))) { + $url_parts['path'] = preg_replace('/^\/'.preg_quote($host_base_dir_tokens, '/').'(\/|$)/i', '${1}', $url_parts['path']); + } + } + return $url = $this->unParseUrl($url_parts); + } + + /** + * Filters a URL in order to remove domain mapping. + * + * @since 150821 Improving multisite compat. + * + * @param string $url The input URL to filter. + * + * @return string The filtered URL; else the original URL. + * + * @note The return value of this function is NOT cached, but inner portions are. + */ + public function domainMappingReverseUrlFilter($url) + { + $original_url = (string) $url; // Preserve. + $url = trim((string) $url); + + if (!is_multisite() || !$this->canConsiderDomainMapping()) { + return $original_url; // Not possible. + } + if (!$url || !($url_parts = $this->parseUrl($url))) { + return $original_url; // Not possible. + } + if (empty($url_parts['host'])) { + return $original_url; // Not possible. + } + if (!($blog_id = $this->domainMappingBlogId('', $url_parts['host']))) { + return $original_url; // No a domain in the map. + } + if (!($blog_details = $this->blogDetails($blog_id))) { + return $original_url; // Not possible. + } + $url_parts['host'] = $blog_details->domain; // Filter the URL now. + if (($host_base_dir_tokens = trim($this->hostBaseDirTokens(false, false, $blog_details->path), '/'))) { + $url_parts['path'] = '/'.$host_base_dir_tokens.'/'.ltrim(@$url_parts['path'], '/'); + } + return $url = $this->unParseUrl($url_parts); + } + + /** + * Converts a host into a mapped blog ID. + * + * @since 150821 Improving multisite compat. + * + * @param string $url URL containing the domain to convert. + * @param string $domain The domain to convert. Override URL is provided. + * + * @return int The mapped blog ID; else `0` on failure. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function domainMappingBlogId($url = '', $domain = '') + { + $domain = (string) $domain; + $url = $domain ? '' : (string) $url; + + if (!is_multisite() || !$this->canConsiderDomainMapping()) { + return 0; // Not possible/applicable. + } + if ($url === 'network' || $domain === 'network') { + $domain = (string) get_current_site()->domain; + } + if (!$domain && $url && $url !== 'network') { + $domain = $this->parseUrl($url, PHP_URL_HOST); + } + if (!$url && !$domain && ($blog_details = $this->blogDetails())) { + $domain = $blog_details->domain; + } + $domain = strtolower(preg_replace('/^www\./i', '', $domain)); + + if (!$domain || strpos($domain, '.') === false) { + return 0; // Not possible. + } + if (!is_null($blog_id = &$this->staticKey('domainMappingBlogId', $domain))) { + return $blog_id; // Already cached this. + } + $wpdb = $this->wpdb(); // WordPress database class. + $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. + $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? + + if (!$enforcing_primary_domain) { + $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC, `active` DESC LIMIT 1'); + } else { + $blog_id = (integer) $wpdb->get_var('SELECT `blog_id` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') AND `active` = \'1\' ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1'); + } + $wpdb->suppress_errors($suppressing_errors); // Restore. + + return $blog_id = (integer) $blog_id; + } + + /** + * Converts a blog ID into a mapped domain. + * + * @since 150821 Improving multisite compat. + * + * @param int $blog_id The blog ID. + * @param bool $fallback Fallback on blog's domain? + * + * @return string The mapped domain, else an empty string. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function domainMappingBlogDomain($blog_id = 0, $fallback = false) + { + if (!is_multisite() || !$this->canConsiderDomainMapping()) { + return ''; // Not possible/applicable. + } + if (($blog_id = (integer) $blog_id) < 0) { + $blog_id = (integer) get_current_site()->blog_id; + } + if (!$blog_id) { + $blog_id = (integer) get_current_blog_id(); + } + if (!$blog_id || $blog_id < 0) { + return ''; // Not possible. + } + if (!is_null($domain = &$this->staticKey('domainMappingBlogDomain', $blog_id))) { + return $domain; // Already cached this. + } + $wpdb = $this->wpdb(); // WordPress database class. + $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. + $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? + + if (!$enforcing_primary_domain) { + if ($this->isDomainMapping() === $blog_id) { + $domain = $this->hostToken(); + $domain = preg_replace('/^www\./i', '', $domain); + $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IN(\''.esc_sql('www.'.$domain).'\', \''.esc_sql($domain).'\') ORDER BY CHAR_LENGTH(`domain`) DESC LIMIT 1'); + } elseif (($domains = $this->domainMappingBlogDomains($blog_id))) { + $domain = $domains[0]; // Use the first of all possible domains. + } + } else { // A single primary domain in this case; i.e., `active` = primary. + $domain = (string) $wpdb->get_var('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\' LIMIT 1'); + } + if (!$domain && $fallback && ($blog_details = $this->blogDetails($blog_id))) { + $domain = $blog_details->domain; // Use original domain. + } + $wpdb->suppress_errors($suppressing_errors); // Restore. + + return $domain = strtolower((string) $domain); + } + + /** + * Converts a blog ID into mapped domains (plural). + * + * @since 150821 Improving multisite compat. + * + * @param int $blog_id The blog ID. + * + * @return array Mapped domains; else an empty array. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function domainMappingBlogDomains($blog_id = 0) + { + if (!is_multisite() || !$this->canConsiderDomainMapping()) { + return []; // Not possible/applicable. + } + if (($blog_id = (integer) $blog_id) < 0) { + $blog_id = (integer) get_current_site()->blog_id; + } + if (!$blog_id) { + $blog_id = (integer) get_current_blog_id(); + } + if (!$blog_id || $blog_id < 0) { + return []; // Not possible. + } + if (!is_null($domains = &$this->staticKey('domainMappingBlogDomains', $blog_id))) { + return $domains; // Already cached this. + } + $wpdb = $this->wpdb(); // WordPress database class. + $suppressing_errors = $wpdb->suppress_errors(); // In case table has not been created yet. + $enforcing_primary_domain = !get_site_option('dm_no_primary_domain'); // Enforcing primary domain? + + if (!$enforcing_primary_domain) { // Not enforcing a primary domain, so let's pull all of the domains. + $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' ORDER BY `active` DESC'); + } else { // Primary domains in this case; i.e., `active` = primary. + $domains = $wpdb->get_col('SELECT `domain` FROM `'.esc_sql($wpdb->base_prefix.'domain_mapping').'` WHERE `blog_id` = \''.esc_sql($blog_id).'\' AND `domain` IS NOT NULL AND `domain` != \'\' AND `active` = \'1\''); + } + $wpdb->suppress_errors($suppressing_errors); // Restore. + + return $domains = array_unique(array_map('strtolower', (array) $domains)); + } +} diff --git a/src/includes/traits/Shared/EscapeUtils.php b/src/includes/traits/Shared/EscapeUtils.php new file mode 100644 index 0000000..ffe5d4e --- /dev/null +++ b/src/includes/traits/Shared/EscapeUtils.php @@ -0,0 +1,23 @@ + `1`. + * + * @return string Escaped string; e.g. `Raam\'s the lead developer`. + */ + public function escSq($string, $times = 1) + { + return str_replace("'", str_repeat('\\', abs($times))."'", (string) $string); + } +} diff --git a/src/includes/traits/Shared/FsUtils.php b/src/includes/traits/Shared/FsUtils.php new file mode 100644 index 0000000..03d399c --- /dev/null +++ b/src/includes/traits/Shared/FsUtils.php @@ -0,0 +1,335 @@ +[a-zA-Z0-9]+)\:\/\//', $dir_file, $stream_wrapper)) { + $dir_file = preg_replace('/^(?P[a-zA-Z0-9]+)\:\/\//', '', $dir_file); + } + } + if (strpos($dir_file, ':' !== false)) { + if (preg_match('/^(?P[a-zA-Z])\:[\/\\\\]/', $dir_file)) { + $dir_file = preg_replace_callback('/^(?P[a-zA-Z])\:[\/\\\\]/', create_function('$m', 'return strtoupper($m[0]);'), $dir_file); + } + } + $dir_file = preg_replace('/\/+/', '/', str_replace([DIRECTORY_SEPARATOR, '\\', '/'], '/', $dir_file)); + $dir_file = ($allow_trailing_slash) ? $dir_file : rtrim($dir_file, '/'); // Strip trailing slashes. + + if (!empty($stream_wrapper[0])) { + $dir_file = strtolower($stream_wrapper[0]).$dir_file; + } + return $dir_file; // Normalized now. + } + + /** + * Acquires system tmp directory path. + * + * @since 150422 Rewrite. + * + * @return string System tmp directory path; else an empty string. + */ + public function getTmpDir() + { + if (!is_null($dir = &$this->staticKey('getTmpDir'))) { + return $dir; // Already cached this. + } + $possible_dirs = []; // Initialize. + + if (defined('WP_TEMP_DIR')) { + $possible_dirs[] = (string) WP_TEMP_DIR; + } + if ($this->functionIsPossible('sys_get_temp_dir')) { + $possible_dirs[] = (string) sys_get_temp_dir(); + } + $possible_dirs[] = (string) ini_get('upload_tmp_dir'); + + if (!empty($_SERVER['TEMP'])) { + $possible_dirs[] = (string) $_SERVER['TEMP']; + } + if (!empty($_SERVER['TMPDIR'])) { + $possible_dirs[] = (string) $_SERVER['TMPDIR']; + } + if (!empty($_SERVER['TMP'])) { + $possible_dirs[] = (string) $_SERVER['TMP']; + } + if (stripos(PHP_OS, 'win') === 0) { + $possible_dirs[] = 'C:/Temp'; + } + if (stripos(PHP_OS, 'win') !== 0) { + $possible_dirs[] = '/tmp'; + } + if (defined('WP_CONTENT_DIR')) { + $possible_dirs[] = (string) WP_CONTENT_DIR; + } + foreach ($possible_dirs as $_key => $_dir) { + if (($_dir = trim((string) $_dir)) && @is_dir($_dir) && @is_writable($_dir)) { + return $dir = $this->nDirSeps($_dir); + } + } + unset($_key, $_dir); // Housekeeping. + + return $dir = ''; + } + + /** + * Finds absolute server path to `/wp-config.php` file. + * + * @since 150422 Rewrite. + * + * @return string Absolute server path to `/wp-config.php` file; + * else an empty string if unable to locate the file. + */ + public function findWpConfigFile() + { + if (!is_null($file = &$this->staticKey('findWpConfigFile'))) { + return $file; // Already cached this. + } + $file = ''; // Initialize. + + if (is_file($abspath_wp_config = ABSPATH.'wp-config.php')) { + $file = $abspath_wp_config; + } elseif (is_file($dirname_abspath_wp_config = dirname(ABSPATH).'/wp-config.php')) { + $file = $dirname_abspath_wp_config; + } + return $file; + } + + /** + * Adds a tmp name suffix to a directory/file path. + * + * @since 150422 Rewrite. + * + * @param string $dir_file An input directory or file path. + * + * @return string The original `$dir_file` with a tmp name suffix. + */ + public function addTmpSuffix($dir_file) + { + $dir_file = (string) $dir_file; + $dir_file = rtrim($dir_file, DIRECTORY_SEPARATOR.'\\/'); + + return $dir_file.'-'.str_replace('.', '', uniqid('', true)).'-tmp'; + } + + /** + * Recursive directory iterator based on a regex pattern. + * + * @since 150422 Rewrite. + * + * @param string $dir An absolute server directory path. + * @param string $regex A regex pattern; compares to each full file path. + * + * @return \RegexIterator Navigable with {@link \foreach()} where each item + * is a {@link \RecursiveDirectoryIterator}. + */ + public function dirRegexIteration($dir, $regex = '') + { + $dir = (string) $dir; + $regex = (string) $regex; + + $dir_iterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS); + $iterator_iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST); + + if ($regex && $regex !== '/.*/' && $regex !== '/.+/') { // Apply regex filter? + // @TODO Optimize calls to this method in order to avoid the regex iterator when not necessary. + return new \RegexIterator($iterator_iterator, $regex, \RegexIterator::MATCH, \RegexIterator::USE_KEY); + } + return $iterator_iterator; // Iterate everything. + } + + /** + * Abbreviated byte notation for file sizes. + * + * @since 151002 Adding a few statistics. + * + * @param float $bytes File size in bytes. A (float) value. + * @param int $precision Number of decimals to use. + * + * @return string Byte notation. + */ + public function bytesAbbr($bytes, $precision = 2) + { + $bytes = max(0.0, (float) $bytes); + $precision = max(0, (integer) $precision); + $units = ['bytes', 'kbs', 'MB', 'GB', 'TB']; + + $power = floor(($bytes ? log($bytes) : 0) / log(1024)); + $abbr_bytes = round($bytes / pow(1024, $power), $precision); + $abbr = $units[min($power, count($units) - 1)]; + + if ($abbr_bytes === (float) 1 && $abbr === 'bytes') { + $abbr = 'byte'; // Quick fix. + } elseif ($abbr_bytes === (float) 1 && $abbr === 'kbs') { + $abbr = 'kb'; // Quick fix. + } + return $abbr_bytes.' '.$abbr; + } + + /** + * Converts an abbreviated byte notation into bytes. + * + * @since 151002 Adding a few statistics. + * + * @param string $string A string value in byte notation. + * + * @return float A float indicating the number of bytes. + */ + public function abbrBytes($string) + { + $string = (string) $string; + $regex = '/^(?P[0-9\.]+)\s*(?Pbytes|byte|kbs|kb|k|mb|m|gb|g|tb|t)$/i'; + + if (!preg_match($regex, $string, $_m)) { + return (float) 0; + } + $value = (float) $_m['value']; + $modifier = strtolower($_m['modifier']); + unset($_m); // Housekeeping. + + switch ($modifier) { + case 't': + case 'tb': + $value *= 1024; + // Fall through. + case 'g': + case 'gb': + $value *= 1024; + // Fall through. + case 'm': + case 'mb': + $value *= 1024; + // Fall through. + case 'k': + case 'kb': + case 'kbs': + $value *= 1024; + } + return (float) $value; + } + + /** + * Directory stats. + * + * @since 151002 Adding a few statistics. + * + * @param string $dir An absolute server directory path. + * @param string $regex A regex pattern; compares to each full file path. + * @param bool $include_paths Include array of all scanned file paths? + * @param bool $check_disk Also check disk statistics? + * @param bool $no_cache Do not read/write cache? + * + * @return array Directory stats. + */ + public function getDirRegexStats($dir, $regex = '', $include_paths = false, $check_disk = true, $no_cache = false) + { + $dir = (string) $dir; // Force string. + $cache_keys = [$dir, $regex, $include_paths, $check_disk]; + if (!$no_cache && !is_null($stats = &$this->staticKey('getDirRegexStats', $cache_keys))) { + return $stats; // Already cached this. + } + $stats = [ + 'total_size' => 0, + 'total_resources' => 0, + 'total_links_files' => 0, + + 'total_links' => 0, + 'link_subpaths' => [], + + 'total_files' => 0, + 'file_subpaths' => [], + + 'total_dirs' => 0, + 'dir_subpaths' => [], + + 'disk_total_space' => 0, + 'disk_free_space' => 0, + ]; + if (!$dir || !is_dir($dir)) { + return $stats; // Not possible. + } + $short_name_lc = strtolower(SHORT_NAME); // Once only. + + foreach ($this->dirRegexIteration($dir, $regex) as $_resource) { + $_resource_sub_path = $_resource->getSubpathname(); + $_resource_basename = basename($_resource_sub_path); + + if ($_resource_basename === '.DS_Store') { + continue; // Ignore `.htaccess`. + } + if ($_resource_basename === '.htaccess') { + continue; // Ignore `.htaccess`. + } + if (stripos($_resource_sub_path, $short_name_lc.'-') === 0) { + continue; // Ignore [SHORT_NAME] files in base. + } + switch ($_resource->getType()) { // `link`, `file`, `dir`. + case 'link': + if ($include_paths) { + $stats['link_subpaths'][] = $_resource_sub_path; + } + ++$stats['total_resources']; + ++$stats['total_links_files']; + ++$stats['total_links']; + + break; // Break switch. + + case 'file': + if ($include_paths) { + $stats['file_subpaths'][] = $_resource_sub_path; + } + $stats['total_size'] += $_resource->getSize(); + ++$stats['total_resources']; + ++$stats['total_links_files']; + ++$stats['total_files']; + + break; // Break switch. + + case 'dir': + if ($include_paths) { + $stats['dir_subpaths'][] = $_resource_sub_path; + } + ++$stats['total_resources']; + ++$stats['total_dirs']; + + break; // Break switch. + } + } + unset($_resource, $_resource_sub_path, $_resource_basename); // Housekeeping. + + if ($check_disk) { // Check disk also? + $stats['disk_total_space'] = disk_total_space($dir); + $stats['disk_free_space'] = disk_free_space($dir); + } + return $stats; + } + + /** + * Apache `.htaccess` rules that deny public access to the contents of a directory. + * + * @since 150422 Rewrite. + * + * @type string `.htaccess` fules. + */ + public $htaccess_deny = "\n\tRequire all denied\n\n\n\tdeny from all\n"; +} diff --git a/src/includes/traits/Shared/HookUtils.php b/src/includes/traits/Shared/HookUtils.php new file mode 100644 index 0000000..eeba36c --- /dev/null +++ b/src/includes/traits/Shared/HookUtils.php @@ -0,0 +1,281 @@ +hookId($function); + + $this->hooks[$hook][$priority][$hook_id] = [ + 'function' => $function, + 'accepted_args' => $accepted_args, + ]; + return true; // Always returns true. + } + + /** + * Adds a new action hook. + * + * @since 150422 Rewrite. + * + * @return bool This always returns a `TRUE` value. + */ + public function addAction() + { + return call_user_func_array([$this, 'addHook'], func_get_args()); + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for addAction() + */ + public function add_action() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'addAction'], func_get_args()); + } + + /** + * Adds a new filter. + * + * @since 150422 Rewrite. + * + * @return bool This always returns a `TRUE` value. + */ + public function addFilter() + { + return call_user_func_array([$this, 'addHook'], func_get_args()); + } + + // @codingStandardsIgnoreStart + /* + * Back compat. alias for addFilter() + */ + public function add_filter() + { // @codingStandardsIgnoreEnd + return call_user_func_array([$this, 'addFilter'], func_get_args()); + } + + /** + * Removes a hook (works with both actions & filters). + * + * @since 150422 Rewrite. + * + * @param string $hook The name of a hook to remove. + * @param string|callable|mixed $function A string or a callable. + * @param int $priority Hook priority; defaults to `10`. + * + * @return bool `TRUE` if removed; else `FALSE` if not removed for any reason. + */ + public function removeHook($hook, $function, $priority = 10) + { + $hook = (string) $hook; + if (stripos($hook, 'zencache') === 0) { + $hook = GLOBAL_NS.substr($hook, strlen('zencache')); + } + $priority = (integer) $priority; + $hook_id = $this->hookId($function); + + if (!isset($this->hooks[$hook][$priority][$hook_id])) { + return false; // Nothing to remove. + } + unset($this->hooks[$hook][$priority][$hook_id]); + + if (!$this->hooks[$hook][$priority]) { + unset($this->hooks[$hook][$priority]); + } + return true; // Existed before it was removed. + } + + /** + * Removes an action. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if removed; else `FALSE` if not removed for any reason. + */ + public function removeAction() + { + return call_user_func_array([$this, 'removeHook'], func_get_args()); + } + + /** + * Removes a filter. + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if removed; else `FALSE` if not removed for any reason. + */ + public function removeFilter() + { + return call_user_func_array([$this, 'removeHook'], func_get_args()); + } + + /** + * Runs any callables attached to an action. + * + * @since 150422 Rewrite. + * + * @param string $hook The name of an action hook. + */ + public function doAction($hook) + { + $hook = (string) $hook; + if (empty($this->hooks[$hook])) { + return; // No hooks. + } + $hook_actions = $this->hooks[$hook]; + $args = func_get_args(); + ksort($hook_actions); + + foreach ($hook_actions as $_hook_action) { + foreach ($_hook_action as $_action) { + if (!isset($_action['function'], $_action['accepted_args'])) { + continue; // Not a valid filter in this case. + } + call_user_func_array($_action['function'], array_slice($args, 1, $_action['accepted_args'])); + } + } + unset($_hook_action, $_action); // Housekeeping. + } + + /** + * Runs any callables attached to a filter. + * + * @since 150422 Rewrite. + * + * @param string $hook The name of a filter hook. + * @param mixed $value The value to filter. + * + * @return mixed The filtered `$value`. + */ + public function applyFilters($hook, $value) + { + $hook = (string) $hook; + if (empty($this->hooks[$hook])) { + return $value; // No hooks. + } + $hook_filters = $this->hooks[$hook]; + $args = func_get_args(); + ksort($hook_filters); + + foreach ($hook_filters as $_hook_filter) { + foreach ($_hook_filter as $_filter) { + if (!isset($_filter['function'], $_filter['accepted_args'])) { + continue; // Not a valid filter in this case. + } + $args[1] = $value; // Continously update the argument `$value`. + $value = call_user_func_array($_filter['function'], array_slice($args, 1, $_filter['accepted_args'])); + } + } + unset($_hook_filter, $_filter); // Housekeeping. + + return $value; // With applied filters. + } + + /** + * Does an action w/ back compat. for ZenCache. + * + * @since 150422 Rewrite. + * + * @param string $hook The hook to apply. + */ + public function doWpAction($hook) + { + $hook = (string) $hook; + $args = func_get_args(); + call_user_func_array('do_action', $args); + + if (stripos($hook, GLOBAL_NS) === 0) { + $zencache_filter = 'zencache'.substr($hook, strlen(GLOBAL_NS)); + $zencache_args = $args; // Use a copy of the args. + $zencache_args[0] = $zencache_filter; + call_user_func_array('do_action', $zencache_args); + } + } + + /** + * Applies filters w/ back compat. for ZenCache. + * + * @since 150422 Rewrite. + * + * @param string $hook The hook to apply. + * + * @return mixed The filtered value. + */ + public function applyWpFilters($hook) + { + $hook = (string) $hook; + $args = func_get_args(); + $value = call_user_func_array('apply_filters', $args); + + if (stripos($hook, GLOBAL_NS) === 0) { + $zencache_hook = 'zencache'.substr($hook, strlen(GLOBAL_NS)); + $zencache_args = $args; // Use a copy of the args. + $zencache_args[0] = $zencache_hook; + $zencache_args[1] = $value; // Filtered value. + $value = call_user_func_array('apply_filters', $zencache_args); + } + return $value; // Filtered value. + } +} diff --git a/src/includes/traits/Shared/HttpUtils.php b/src/includes/traits/Shared/HttpUtils.php new file mode 100644 index 0000000..f64b7d8 --- /dev/null +++ b/src/includes/traits/Shared/HttpUtils.php @@ -0,0 +1,177 @@ +staticKey('httpProtocol'))) { + return $protocol; // Already cached this. + } + if (!empty($_SERVER['SERVER_PROTOCOL']) && is_string($_SERVER['SERVER_PROTOCOL'])) { + $protocol = strtoupper($_SERVER['SERVER_PROTOCOL']); + } + if (!$protocol || stripos($protocol, 'HTTP/') !== 0) { + $protocol = 'HTTP/1.0'; // Default value. + } + return $protocol; + } + + /** + * PHP {@link headers_list()} + HTTP status. + * + * @since 150422 Rewrite. + * + * @return array PHP {@link headers_list()} + HTTP status. + * + * @warning Do NOT call until end of script execution. + */ + public function headersList() + { + if (!is_null($headers = &$this->staticKey('headersList'))) { + return $headers; // Already cached this. + } + $headers = headers_list(); // Lacks status. + + if (($status = (string) $this->httpStatus())) { + array_unshift($headers, $this->httpProtocol().' '.$status); + } + return $headers; + } + + /** + * PHP {@link headers_list()} + HTTP status. + * + * @since 150422 Rewrite. + * + * @return array PHP {@link headers_list()} + HTTP status. + * + * @warning Do NOT call until end of script execution. + */ + public function cacheableHeadersList() + { + if (!is_null($headers = &$this->staticKey('cacheableHeadersList'))) { + return $headers; // Already cached this. + } + $headers = headers_list(); // Lacks status. + + $cacheable_headers = [ + 'Access-Control-Allow-Origin', + 'Accept-Ranges', + 'Age', + 'Allow', + 'Cache-Control', + 'Connection', + 'Content-Encoding', + 'Content-Language', + 'Content-Length', + 'Content-Location', + 'Content-MD5', + 'Content-Disposition', + 'Content-Range', + 'Content-Type', + 'Date', + 'ETag', + 'Expires', + 'Last-Modified', + 'Link', + 'Location', + 'P3P', + 'Pragma', + 'Proxy-Authenticate', + 'Refresh', + 'Retry-After', + 'Server', + 'Status', + 'Strict-Transport-Security', + 'Trailer', + 'Transfer-Encoding', + 'Upgrade', + 'Vary', + 'Via', + 'Warning', + 'WWW-Authenticate', + 'X-Frame-Options', + 'Public-Key-Pins', + 'X-XSS-Protection', + 'Content-Security-Policy', + 'X-Content-Security-Policy', + 'X-WebKit-CSP', + 'X-Content-Type-Options', + 'X-Powered-By', + 'X-UA-Compatible', + ]; + $cacheable_headers = array_map('strtolower', $cacheable_headers); + + foreach ($headers as $_key => $_header) { + $_header = strtolower((string) strstr($_header, ':', true)); + if (!$_header || !in_array($_header, $cacheable_headers, true)) { + unset($headers[$_key]); + } + } + unset($_key, $_header); // Housekeeping. + + if (($status = (string) $this->httpStatus())) { + array_unshift($headers, $this->httpProtocol().' '.$status); + } + return $headers; + } + + /** + * HTTP status code. + * + * @since 150422 Rewrite. + * + * @return int HTTP status code. + * + * @warning Do NOT call until end of script execution. + * + * @note Automatically updates HTTP status-related flags. + */ + public function httpStatus() + { + if (!is_null($status = &$this->staticKey('httpStatus'))) { + return $status; // Already cached this. + } + $status = 0; // Initialize. + $has_property_is_404 = property_exists($this, 'is_404'); + $has_property_http_status = property_exists($this, 'http_status'); + + if ($has_property_is_404 && $this->is_404) { + $status = 404; // WordPress said so. + } elseif (($code = (integer) http_response_code())) { + $status = (integer) $code; // {@link \http_response_code()} available since PHP v5.4. + } elseif ($has_property_http_status && (integer) $this->http_status) { + $status = (integer) $this->http_status; // {@link \status_header()} filter. + } + if ($status && $has_property_http_status) { + $this->http_status = $status; // Prefer over {@link status_header()}. + } + if ($status === 404 && $has_property_is_404) { + $this->is_404 = true; // Prefer over {@link is_404()}. + } + return $status; + } + + /** + * Sends no-cache headers. + * + * @since 151220 Enhancing no-cache headers. + */ + public function sendNoCacheHeaders() + { + header_remove('Last-Modified'); + header('Expires: Wed, 11 Jan 1984 05:00:00 GMT'); + header('Cache-Control: no-cache, must-revalidate, max-age=0'); + header('Pragma: no-cache'); + } +} diff --git a/src/includes/traits/Shared/I18nUtils.php b/src/includes/traits/Shared/I18nUtils.php new file mode 100644 index 0000000..f57d369 --- /dev/null +++ b/src/includes/traits/Shared/I18nUtils.php @@ -0,0 +1,52 @@ +staticKey('currentIp'))) { + return $ip; // Already cached this. + } + $sources = [ + 'HTTP_CF_CONNECTING_IP', + 'HTTP_CLIENT_IP', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED', + 'HTTP_X_CLUSTER_CLIENT_IP', + 'HTTP_FORWARDED_FOR', + 'HTTP_FORWARDED', + 'HTTP_VIA', + 'REMOTE_ADDR', + ]; + $sources = $this->applyFilters(GLOBAL_NS.'\\share::current_ip_sources', $sources); + $sources = $this->applyFilters(GLOBAL_NS.'_current_ip_sources', $sources); + + $prioritize_remote_addr = false; // Off by default; can be filtered however. + $prioritize_remote_addr = $this->applyFilters(GLOBAL_NS.'\\share::current_ip_prioritize_remote_addr', $prioritize_remote_addr); + $prioritize_remote_addr = $this->applyFilters(GLOBAL_NS.'_current_ip_prioritize_remote_addr', $prioritize_remote_addr); + + if (!empty($_SERVER['REMOTE_ADDR']) && $prioritize_remote_addr) { + if (($_valid_public_ip = $this->validPublicIp((string) $_SERVER['REMOTE_ADDR']))) { + return $ip = $_valid_public_ip; + } + unset($_valid_public_ip); // Housekeeping. + } + foreach ($sources as $_key => $_source) { + if (!empty($_SERVER[$_source])) { + if (($_valid_public_ip = $this->validPublicIp((string) $_SERVER[$_source]))) { + return $ip = $_valid_public_ip; + } + } + unset($_key, $_source, $_valid_public_ip); // Housekeeping. + } + if (!empty($_SERVER['REMOTE_ADDR'])) { + return $ip = strtolower((string) $_SERVER['REMOTE_ADDR']); + } + return $ip = 'unknown'; // Not possible. + } + + /** + * Gets a valid/public IP address. + * + * @since 150422 Rewrite. + * + * @param string $list_of_possible_ips A single IP, or a comma-delimited list of IPs. + * + * @return string A valid/public IP address (if one is found), else an empty string. + * + * @note This supports both IPv4 and IPv6 addresses. + * @note See my tests against this here: http://3v4l.org/fVWUp + */ + public function validPublicIp($list_of_possible_ips) + { + if (!$list_of_possible_ips || !is_string($list_of_possible_ips)) { + return ''; // Empty or invalid data. + } + if (!($list_of_possible_ips = trim($list_of_possible_ips))) { + return ''; // Not possible; i.e., empty string. + } + foreach (preg_split('/[\s;,]+/', $list_of_possible_ips, -1, PREG_SPLIT_NO_EMPTY) as $_key => $_possible_ip) { + if (($_valid_public_ip = filter_var(strtolower($_possible_ip), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) { + return $_valid_public_ip; // A valid public IPv4 or IPv6 address. + } + } + unset($_key, $_possible_ip, $_valid_public_ip); // Housekeeping. + + return ''; // Default return value. + } +} diff --git a/src/includes/traits/Shared/PatternUtils.php b/src/includes/traits/Shared/PatternUtils.php new file mode 100644 index 0000000..101f8ce --- /dev/null +++ b/src/includes/traits/Shared/PatternUtils.php @@ -0,0 +1,55 @@ +strReplaceOnce($needle, $replace, $haystack, true); + } +} diff --git a/src/includes/traits/Shared/ServerUtils.php b/src/includes/traits/Shared/ServerUtils.php new file mode 100644 index 0000000..24a16e5 --- /dev/null +++ b/src/includes/traits/Shared/ServerUtils.php @@ -0,0 +1,73 @@ +staticKey('isApache'))) { + return $is; // Already cached this. + } + if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { + if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) { + return $is = true; + } + if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false) { + return $is = true; + } + } + return $is = false; + } + + /** + * Is running on Nginx? + * + * @since 151002 This is Nginx? + * + * @return bool True if running Nginx. + */ + public function isNginx() + { + if (!is_null($is = &$this->staticKey('isNginx'))) { + return $is; // Already cached this. + } + if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { + if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) { + return $is = true; + } + } + return $is = false; + } + + /** + * Is running on Windows IIS? + * + * @since 151002 This is Windows IIS? + * + * @return bool True if running Windows IIS. + */ + public function isIis() + { + if (!is_null($is = &$this->staticKey('isIis'))) { + return $is; // Already cached this. + } + if (!empty($_SERVER['SERVER_SOFTWARE']) && is_string($_SERVER['SERVER_SOFTWARE'])) { + if (stripos($_SERVER['SERVER_SOFTWARE'], 'microsoft-iis') !== false) { + return $is = true; + } + if (stripos($_SERVER['SERVER_SOFTWARE'], 'expressiondevserver') !== false) { + return $is = true; + } + } + return $is = false; + } +} diff --git a/src/includes/traits/Shared/StringUtils.php b/src/includes/traits/Shared/StringUtils.php new file mode 100644 index 0000000..72abe4b --- /dev/null +++ b/src/includes/traits/Shared/StringUtils.php @@ -0,0 +1,94 @@ + &$_value) { + $_value = $this->clip($_value, $max_length, $force_ellipsis); + } + unset($_key, $_value); // Housekeeping. + + return $value; + } + if (!($string = (string) $value)) { + return $string; // Empty. + } + $max_length = max(4, $max_length); + + $string = strip_tags($string); + $string = preg_replace('/\s+/', ' ', strip_tags($string)); + $string = trim($string); // Trim it up now. + + if (strlen($string) > $max_length) { + $string = (string) substr($string, 0, $max_length - 3).'...'; + } elseif ($force_ellipsis && strlen($string) + 3 > $max_length) { + $string = (string) substr($string, 0, $max_length - 3).'...'; + } else { + $string .= $force_ellipsis ? '...' : ''; + } + return $string; + } + + /** + * Mid-clips string(s) to X chars deeply. + * + * @since 151114 Adding string utils. + * + * @param mixed $value Any input value. + * @param int $max_length Defaults to a value of `80`. + * + * @return string|array|object Mid-clipped value. + */ + public function midClip($value, $max_length = 80) + { + if (is_array($value) || is_object($value)) { + foreach ($value as $_key => &$_value) { + $_value = $this->midClip($_value, $max_length); + } + unset($_key, $_value); // Housekeeping. + + return $value; + } + if (!($string = (string) $value)) { + return $string; // Empty. + } + $max_length = max(4, $max_length); + + $string = strip_tags($string); + $string = preg_replace('/\s+/', ' ', strip_tags($string)); + $string = trim($string); // Trim it up now. + + if (strlen($string) <= $max_length) { + return $string; // Nothing to do. + } + $full_string = $string; + $half_max_length = floor($max_length / 2); + + $first_clip = $half_max_length - 3; + $string = $first_clip >= 1 // Something? + ? substr($full_string, 0, $first_clip).'...' + : '...'; // Ellipsis only. + + $second_clip = strlen($full_string) - ($max_length - strlen($string)); + $string .= $second_clip >= 0 && $second_clip >= $first_clip + ? substr($full_string, $second_clip) : ''; + + return $string; + } +} diff --git a/src/includes/traits/Shared/SysUtils.php b/src/includes/traits/Shared/SysUtils.php new file mode 100644 index 0000000..e1451ec --- /dev/null +++ b/src/includes/traits/Shared/SysUtils.php @@ -0,0 +1,100 @@ +cacheKey('sysLoadAverages'))) { + return $averages; // Already cached these. + } + if (!$this->functionIsPossible('sys_getloadavg')) { + return $averages = []; + } + if (!is_array($averages = sys_getloadavg()) || !$averages) { + return $averages = []; + } + $averages = array_map('floatval', $averages); + $averages = array_slice($averages, 0, 3); + // i.e., 1m, 5m, 15m; see: + + return $averages; + } + + /** + * System memory info. + * + * @since 151002 Adding cache directory statistics. + * + * @return \stdClass|bool System memory info. + */ + public function sysMemoryStatus() + { + if (!is_null($status = &$this->cacheKey('sysMemoryStatus'))) { + return $status; // Already cached this. + } + if (!$this->functionIsPossible('shell_exec')) { + return $status = false; + } + if (!($free = trim((string) @shell_exec('free')))) { + return $status = false; + } + if (!($free_lines = explode("\n", $free))) { + return $status = false; + } + if (empty($free_lines[1])) { + return $status = false; + } + if (!($memory = explode(' ', $free_lines[1]))) { + return $status = false; + } + if (!($memory = array_merge(array_filter($memory)))) { + return $status = false; + } + if (!isset($memory[1], $memory[2])) { + return $status = false; + } + if (($total = (integer) $memory[1]) <= 0) { + return $status = false; + } + $used = (integer) $memory[2]; + $percent = $used / $total * 100; + $percentage = sprintf(__('%s%%', 'comet-cache'), number_format($percent, 2, '.', '')); + $status = (object) compact('total', 'used', 'percent', 'percentage'); + + return $status; + } + + /** + * System opcache status/details. + * + * @since 151002 Adding cache directory statistics. + * + * @return \stdClass|bool System opcache status/details. + */ + public function sysOpcacheStatus() + { + if (!is_null($status = &$this->cacheKey('sysOpcacheStatus'))) { + return $status; // Already cached this. + } + if (!$this->functionIsPossible('opcache_get_status')) { + return $status = false; + } + if (!is_array($status = opcache_get_status(false)) || !$status) { + return $status = false; + } + if (empty($status['opcache_enabled'])) { + return $status = false; + } + return json_decode(json_encode($status)); + } +} diff --git a/src/includes/traits/Shared/TokenUtils.php b/src/includes/traits/Shared/TokenUtils.php new file mode 100644 index 0000000..bd257b1 --- /dev/null +++ b/src/includes/traits/Shared/TokenUtils.php @@ -0,0 +1,297 @@ +staticKey('hostToken', [$dashify, $consider_domain_mapping, $consider_domain_mapping_domain]))) { + return $token; // Already cached this. + } + $token = ''; // Initialize token value. + + if (!is_multisite() || $this->isAdvancedCache()) { + $token = (string) $_SERVER['HTTP_HOST']; + } elseif ($consider_domain_mapping && $this->canConsiderDomainMapping()) { + if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) { + $token = $consider_domain_mapping_domain; + } elseif ($this->isDomainMapping()) { + $token = (string) $_SERVER['HTTP_HOST']; + } else { // For the current blog ID. + $token = $this->domainMappingUrlFilter($this->currentUrl()); + $token = $this->parseUrl($token, PHP_URL_HOST); + } + } + if (!$token) { // Use default? + $token = (string) $_SERVER['HTTP_HOST']; + } + if ($token) { // Have token? + $token = strtolower($token); + if ($dashify) { // Dashify it? + $token = preg_replace('/[^a-z0-9]/i', '-', $token); + $token = trim($token, '-'); + } + } + return $token; + } + + /** + * Host for a specific blog. + * + * @since 150821 Improving multisite compat. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`. + * @param bool $consider_domain_mapping Consider? + * @param string $consider_domain_mapping_domain A specific domain? + * @param bool $fallback Fallback on blog's domain when mapping? + * @param int $blog_id For which blog ID? + * + * @return string Host for a specific blog. + * + * @note The return value of this function is NOT cached in support of `switch_to_blog()`. + */ + public function hostTokenForBlog($dashify = false, $consider_domain_mapping = false, $consider_domain_mapping_domain = '', $fallback = false, $blog_id = 0) + { + if (!is_multisite() || $this->isAdvancedCache()) { + return $this->hostToken($dashify, $consider_domain_mapping, $consider_domain_mapping_domain); + } + $token = ''; // Initialize token value. + + if ($consider_domain_mapping && $this->canConsiderDomainMapping()) { + if (($consider_domain_mapping_domain = trim((string) $consider_domain_mapping_domain))) { + $token = $consider_domain_mapping_domain; // Force this value. + } else { + $token = $this->domainMappingBlogDomain($blog_id, $fallback); + } + } elseif (($blog_details = $this->blogDetails($blog_id))) { + $token = $blog_details->domain; // Unmapped domain. + } + if ($token) { // Have token? + $token = strtolower($token); + if ($dashify) { // Dashify it? + $token = preg_replace('/[^a-z0-9]/i', '-', $token); + $token = trim($token, '-'); + } + } + return $token; + } + + /** + * Current site's base directory. + * + * @since 150422 Rewrite. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`. + * @param bool $consider_domain_mapping Consider? + * + * @return string Current site's base directory. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function hostBaseToken($dashify = false, $consider_domain_mapping = false) + { + if (!is_null($token = &$this->staticKey('hostBaseToken', [$dashify, $consider_domain_mapping]))) { + return $token; // Already cached this. + } + $token = '/'; // Assume NOT multisite; or own domain. + + if (!is_multisite()) { + return $token; // Not applicable. + } + if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { + return $token; // Not applicable. + } + if ($consider_domain_mapping && $this->canConsiderDomainMapping()) { + return $token; // Not applicable. + } + if (defined('PATH_CURRENT_SITE')) { + $token = (string) PATH_CURRENT_SITE; + } + $token = trim($token, '\\/'." \t\n\r\0\x0B"); + $token = isset($token[0]) ? '/'.$token.'/' : '/'; + + if ($token !== '/' && $dashify) { + $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); + $token = trim($token, '-'); + } + return $token; + } + + /** + * Current blog's sub-directory. + * + * @since 150422 Rewrite. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9\/]`. + * @param bool $consider_domain_mapping Consider? + * @param string $path Defaults to the current URI path. + * + * @return string Current blog's sub-directory. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function hostDirToken($dashify = false, $consider_domain_mapping = false, $path = null) + { + if (!isset($path)) { // Use current/default path? + $path = (string) $this->parseUrl($_SERVER['REQUEST_URI'], PHP_URL_PATH); + } + $path = '/'.ltrim((string) $path, '/'); // Force leading slash. + + if (!is_null($token = &$this->staticKey('hostDirToken', [$dashify, $consider_domain_mapping, $path]))) { + return $token; // Already cached this. + } + $token = '/'; // Assume NOT multisite; or own domain. + + if (!is_multisite()) { + return $token; // Not applicable. + } + if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { + return $token; // Not applicable. + } + if ($consider_domain_mapping && $this->canConsiderDomainMapping()) { + return $token; // Not applicable. + } + if ($path && $path !== '/' && ($host_base_token = trim($this->hostBaseToken(), '/'))) { + $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path); + } else { + $path_minus_base = $path; // Default value. + } + list($token) = explode('/', trim($path_minus_base, '/')); + $token = trim($token, '\\/'." \t\n\r\0\x0B"); + $token = isset($token[0]) ? '/'.$token.'/' : '/'; + + if ($token !== '/') { // Perhaps NOT the main site? + $blog_paths_file = $this->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths'; + if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) { + $token = '/'; // NOT a real/valid child blog path. + } + } + if ($token !== '/' && $dashify) { + $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); + $token = trim($token, '-'); + } + return $token; + } + + /** + * A blog's sub-directory. + * + * @since 150821 Improving multisite compat. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the token is returned with dashes in place of `[^a-z0-9]`. + * @param bool $consider_domain_mapping Consider? + * @param int $blog_id For which blog ID? + * + * @return string A blog's sub-directory. + * + * @note The return value of this function is NOT cached in support of `switch_to_blog()`. + */ + public function hostDirTokenForBlog($dashify = false, $consider_domain_mapping = false, $blog_id = 0) + { + if (!is_multisite() || $this->isAdvancedCache()) { + return $this->hostDirToken($dashify, $consider_domain_mapping); + } + $token = '/'; // Initialize token value. + + if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { + return $token; // Not applicable. + } + if ($consider_domain_mapping && $this->canConsiderDomainMapping()) { + return $token; // Not applicable. + } + if (($blog_details = $this->blogDetails($blog_id))) { + $path = $blog_details->path; // e.g., `[/base]/path/` (includes base). + if ($path && $path !== '/' && ($host_base_token = trim($this->hostBaseToken(), '/'))) { + $path_minus_base = preg_replace('/^\/'.preg_quote($host_base_token, '/').'(\/|$)/i', '${1}', $path); + } else { + $path_minus_base = $path; // Default value. + } + list($token) = explode('/', trim($path_minus_base, '/')); + } + $token = trim($token, '\\/'." \t\n\r\0\x0B"); + $token = isset($token[0]) ? '/'.$token.'/' : '/'; + + if ($token !== '/') { // Perhaps NOT the main site? + $blog_paths_file = $this->cacheDir().'/'.strtolower(SHORT_NAME).'-blog-paths'; + if (!is_file($blog_paths_file) || !in_array($token, unserialize(file_get_contents($blog_paths_file)), true)) { + $token = '/'; // NOT a real/valid child blog path. + } + } + if ($token !== '/' && $dashify) { + $token = preg_replace('/[^a-z0-9\/]/i', '-', $token); + $token = trim($token, '-'); + } + return $token; + } + + /** + * Current site's base directory & current blog's sub-directory. + * + * @since 150422 Rewrite. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`. + * @param bool $consider_domain_mapping Consider? + * @param string $path Defaults to the current URI path. + * + * @return string Current site's base directory & current blog's sub-directory. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function hostBaseDirTokens($dashify = false, $consider_domain_mapping = false, $path = null) + { + if (!is_null($tokens = &$this->staticKey('hostBaseDirTokens', [$dashify, $consider_domain_mapping, $path]))) { + return $tokens; // Already cached this. + } + $tokens = $this->hostBaseToken($dashify, $consider_domain_mapping); + $tokens .= $this->hostDirToken($dashify, $consider_domain_mapping, $path); + + return $tokens = preg_replace('/\/+/', '/', $tokens); + } + + /** + * A site's base directory & a blog's sub-directory. + * + * @since 150821 Improving multisite compat. + * + * @param bool $dashify Optional, defaults to a `FALSE` value. + * If `TRUE`, the tokens are returned with dashes in place of `[^a-z0-9\/]`. + * @param bool $consider_domain_mapping Consider? + * @param int $blog_id For which blog ID? + * + * @return string A site's base directory & a blog's sub-directory. + * + * @note The return value of this function is NOT cached in support of `switch_to_blog()`. + */ + public function hostBaseDirTokensForBlog($dashify = false, $consider_domain_mapping = false, $blog_id = 0) + { + $tokens = $this->hostBaseToken($dashify, $consider_domain_mapping); + $tokens .= $this->hostDirTokenForBlog($dashify, $consider_domain_mapping, $blog_id); + + return $tokens = preg_replace('/\/+/', '/', $tokens); + } + + +} diff --git a/src/includes/traits/Shared/TrimUtils.php b/src/includes/traits/Shared/TrimUtils.php new file mode 100644 index 0000000..e7a3d69 --- /dev/null +++ b/src/includes/traits/Shared/TrimUtils.php @@ -0,0 +1,40 @@ + &$_values) { + $_values = $this->trimDeep($_values, $chars, $extra_chars); + } + unset($_key, $_values); // Housekeeping. + + return $values; + } + $string = (string) $values; + $chars = (string) $chars; + $extra_chars = (string) $extra_chars; + + $chars = isset($chars[0]) ? $chars : " \r\n\t\0\x0B"; + $chars = $chars.$extra_chars; // Concatenate. + + return trim($string, $chars); + } +} diff --git a/src/includes/traits/Shared/UrlUtils.php b/src/includes/traits/Shared/UrlUtils.php new file mode 100644 index 0000000..1c45164 --- /dev/null +++ b/src/includes/traits/Shared/UrlUtils.php @@ -0,0 +1,140 @@ + -1) { + if (${'//'} && $component === PHP_URL_SCHEME) { + return $part = '//'; + } + return $part = parse_url($url_uri_qsl, $component); + } else { + if (!is_array($parts = parse_url($url_uri_qsl))) { + return $parts = []; + } + if (${'//'}) { + $parts['scheme'] = '//'; + } + return $parts; + } + } + + /** + * Unparses a URL. + * + * @since 150821 Improving multisite compat. + * + * @param array $parts Input URL parts. + * + * @return string Unparsed URL in string format. + */ + public function unParseUrl(array $parts) + { + $scheme = ''; + $host = ''; + $port = ''; + $user = ''; + $pass = ''; + $path = ''; + $query = ''; + $fragment = ''; + + if (!empty($parts['scheme'])) { + if ($parts['scheme'] === '//') { + $scheme = $parts['scheme']; + } else { + $scheme = $parts['scheme'].'://'; + } + } + if (!empty($parts['host'])) { + $host = $parts['host']; + } + if (!empty($parts['port'])) { + $port = ':'.$parts['port']; + } + if (!empty($parts['user'])) { + $user = $parts['user']; + } + if (!empty($parts['pass'])) { + $pass = $parts['pass']; + } + if ($user || $pass) { + $pass .= '@'; + } + if (!empty($parts['path'])) { + $path = '/'.ltrim($parts['path'], '/'); + } + if (!empty($parts['query'])) { + $query = '?'.$parts['query']; + } + if (!empty($parts['fragment'])) { + $fragment = '#'.$parts['fragment']; + } + return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; + } + + /** + * Is the current request over SSL? + * + * @since 150422 Rewrite. + * + * @return bool `TRUE` if the current request is over SSL. + * + * @note The return value of this function is cached to reduce overhead on repeat calls. + */ + public function isSsl() + { + if (!is_null($is = &$this->staticKey('isSsl'))) { + return $is; // Already cached this. + } + if (!empty($_SERVER['SERVER_PORT'])) { + if ((integer) $_SERVER['SERVER_PORT'] === 443) { + return $is = true; + } + } + if (!empty($_SERVER['HTTPS'])) { + if (filter_var($_SERVER['HTTPS'], FILTER_VALIDATE_BOOLEAN)) { + return $is = true; + } + } + if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { + if (strcasecmp((string) $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0) { + return $is = true; + } + } + return $is = false; + } + + /** + * Current URL. + * + * @since 150821 Improving multisite compat. + * + * @return string Current URL. + */ + public function currentUrl() + { + return ($this->isSsl() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; + } +} diff --git a/src/includes/translations/comet-cache.pot b/src/includes/translations/comet-cache.pot deleted file mode 100644 index 806d321..0000000 --- a/src/includes/translations/comet-cache.pot +++ /dev/null @@ -1,1927 +0,0 @@ -# Copyright (C) 2016 Comet Cache -# This file is distributed under the same license as the Comet Cache package. -msgid "" -msgstr "" -"Project-Id-Version: Comet Cache 160227\n" -"Report-Msgid-Bugs-To: http://wordpress.org/tag/comet-cache\n" -"POT-Creation-Date: 2016-02-27 20:13:18+00:00\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" - -#: plugin.php:17 -msgid "NOTICE: Comet Cache Minimum PHP Version" -msgstr "" - -#: plugin.php:18 -msgid "As of December 1st, 2015 Comet Cache requires PHP 5.4 or higher. Your server is currently running PHP v%1$s. You will need to upgrade to PHP 5.4 or higher to run this version of Comet Cache." -msgstr "" - -#: plugin.php:19 -msgid "Learn more about this change here: New Minimum PHP Version: PHP 5.4" -msgstr "" - -#: plugin.php:21 -msgid "Your server is also running the outdated PHP APC extension. Please see: PHP APC Extension No Longer Supported" -msgstr "" - -#: plugin.php:39 -msgid "NOTICE: Comet Cache + PHP APC Extension" -msgstr "" - -#: plugin.php:40 -msgid "As of December 1st, 2015 Comet Cache no longer runs with the outdated PHP APC extension. It appears that you're currently running PHP v%1$s with APC enabled. You will need to follow one of the actions below to run this version of Comet Cache." -msgstr "" - -#: plugin.php:41 -msgid "

Options Available (Action Required):

" -msgstr "" - -#: plugin.php:43 -msgid "Please add ini_set('apc.cache_by_default', false); to the top of your /wp-config.php file. That will get rid of this message and allow Comet Cache to run without issue." -msgstr "" - -#: plugin.php:44 -msgid "Or, contact your web hosting provider and ask about upgrading to PHP v5.5+; which includes the new OPcache extension for PHP. The new OPcache extension replaces APC in modern versions of PHP." -msgstr "" - -#: plugin.php:46 -msgid "To learn more about this change, please see the announcement: PHP APC Extension No Longer Supported" -msgstr "" - -#: plugin.php:66 -msgid "NOTICE: Comet Cache Will Require the PHP mbstring Extension" -msgstr "" - -#: plugin.php:67 -msgid "After March 1st, 2016 Comet Cache will require PHP Multibyte String support. It appears that your site is currently running PHP v%1$s without the mbstring extension enabled. You will need to contact your web hosting company and have them enable the PHP mbstring extension if you want to run the next version of Comet Cache." -msgstr "" - -#: plugin.php:68 -msgid "The mbstring extension provides Multibyte String support to PHP and is required to properly handle UTF-8 characters, which many sites now use. Without Multibyte String support, Comet Cache will be unstable. For that reason we are requiring the mbstring extension to improve reliablity when caching and to prevent your site from experiencing unforeseen issues in the future." -msgstr "" - -#: src/includes/classes/AbsBase.php:116 -msgid "Undefined overload property: `%1$s`." -msgstr "" - -#: src/includes/classes/AbsBase.php:134 -msgid "Refused to set overload property: `%1$s`." -msgstr "" - -#: src/includes/classes/AbsBase.php:151 -msgid "Refused to unset overload property: `%1$s`." -msgstr "" - -#: src/includes/classes/AbsBaseAp.php:57 -msgid "Undefined method/closure: `%1$s`." -msgstr "" - -#: src/includes/classes/Conflicts.php:89 -msgid "Pro" -msgstr "" - -#: src/includes/classes/Conflicts.php:90 -msgid "Lite" -msgstr "" - -#: src/includes/classes/Conflicts.php:95 -msgid "%1$s is NOT running. A conflicting plugin, %2$s, is currently active. Please deactivate the %2$s plugin to clear this message." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:30 -msgid "Wipe Cache (Start Fresh); clears the cache for all sites in this network at once!" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:32 -msgid "Wipe" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:34 -msgid "Clear Cache (Start Fresh)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:34 -msgid "; affects the current site only." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:36 -msgid "Clear" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:39 -msgid "Restore default plugin options? You will lose all of your current settings! Are you absolutely sure about this?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:41 -msgid "Restore" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:43 -msgid "All Panels" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:50 -msgid "Pro Updater" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:51 -#: src/includes/classes/MenuPageOptions.php:73 -msgid "Newsletter" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:52 -#: src/includes/classes/MenuPageOptions.php:74 -msgid "Beta Testers" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:55 -#: src/includes/closures/Plugin/MenuPageUtils.php:120 -msgid "Preview Pro Features" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:56 -msgid "Pro Upgrade" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:62 -msgid "Support" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:65 -msgid "Community Forum" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:67 -msgid "Knowledge Base" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:68 -msgid "Blog" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:80 -msgid "%1$s™ Pro v%2$s" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:83 -#: src/includes/classes/MenuPageOptions.php:93 -msgid "update available" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:85 -#: src/includes/classes/MenuPageOptions.php:95 -msgid "changelog" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:90 -msgid "%1$s™ v%2$s" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:99 -#: src/includes/closures/Plugin/MenuPageUtils.php:73 -#: src/includes/closures/Plugin/MenuPageUtils.php:95 -msgid "Plugin Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:113 -msgid "Options updated successfully." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:118 -msgid "Default options successfully restored." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:123 -msgid "Cache wiped across all sites; recreation will occur automatically over time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:129 -msgid "Cache cleared for main site; recreation will occur automatically over time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:131 -msgid "Cache cleared for this site; recreation will occur automatically over time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:137 -#: src/includes/classes/MenuPageOptions.php:142 -msgid "Failed to update your /.htaccess file automatically. Most likely a permissions error. Please make sure it has permissions 644 or higher (perhaps 666). Once you've done this, please try saving the %1$s options again." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:147 -msgid "It appears that your server is running NGINX and does not support .htaccess rules. Please update your server configuration manually. If you've already updated your NGINX configuration, you can safely ignore this message." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:152 -msgid "Failed to update your /wp-config.php file automatically. Please add the following line to your /wp-config.php file (right after the opening <?php tag; on it's own line).
<?php
define('WP_CACHE', TRUE);
" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:157 -msgid "Failed to update your /wp-config.php file automatically. Please remove the following line from your /wp-config.php file, or set WP_CACHE to a FALSE value.
define('WP_CACHE', TRUE);
" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:163 -msgid "Failed to update your /wp-content/advanced-cache.php file. Cannot write stat file: %1$s/%2$s-advanced-cache. Please be sure this directory exists (and that it's writable): %1$s. Please use directory permissions 755 or higher (perhaps 777). Once you've done this, please try again." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:165 -msgid "Failed to update your /wp-content/advanced-cache.php file. Most likely a permissions error. Please create an empty file here: /wp-content/advanced-cache.php (just an empty PHP file, with nothing in it); give it permissions 644 or higher (perhaps 666). Once you've done this, please try again." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:171 -msgid "Failed to remove your /wp-content/advanced-cache.php file. Most likely a permissions error. Please delete (or empty the contents of) this file: /wp-content/advanced-cache.php." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:176 -msgid "close" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:177 -msgid "Pro Features (Preview) ~ New option panels below. Please explore before upgrading .
NOTE: the free version of %1$s (this lite version) is more-than-adequate for most sites. Please upgrade only if you desire advanced features or would like to support the developer." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:182 -msgid "%1$s is currently disabled; please review options below." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:192 -msgid "Basic Configuration (Required)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:193 -msgid "Review these basic options and %1$s™ will be ready-to-go!" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:201 -msgid "Enable/Disable" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:206 -msgid "%1$s™ = SPEED!!" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:207 -msgid "Yes, enable %1$s™" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:207 -msgid "No, disable." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:208 -msgid "HUGE Time-Saver: Approx. 95%% of all WordPress sites running %1$s, simply enable it here; and that's it :-) No further configuration is necessary (really). All of the other options (down below) are already tuned for the BEST performance on a typical WordPress installation. Simply enable %1$s here and click \"Save All Changes\". If you get any warnings please follow the instructions given. Otherwise, you're good . This plugin is designed to run just fine like it is. Take it for a spin right away; you can always fine-tune things later if you deem necessary." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:211 -msgid "How Can I Tell %1$s is Working?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:212 -msgid "First of all, please make sure that you've enabled %1$s here; then scroll down to the bottom of this page and click \"Save All Changes\". All of the other options (below) are already pre-configured for typical usage. Feel free to skip them all for now. You can go back through all of these later and fine-tune things the way you like them." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:213 -msgid "Once %1$s has been enabled, you'll need to log out (and/or clear browser cookies). By default, cache files are NOT served to visitors who are logged-in, and that includes you too ;-) Cache files are NOT served to recent comment authors either. If you've commented (or replied to a comment lately); please clear your browser cookies before testing." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:214 -msgid "To verify that %1$s is working, navigate your site like a normal visitor would. Right-click on any page (choose View Source), then scroll to the very bottom of the document. At the bottom, you'll find comments that show %1$s stats and information. You should also notice that page-to-page navigation is lightning fast now that %1$s is running; and it gets faster over time!" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:216 -msgid "Yes, enable notes in the source code so I can see it's working (recommended)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:217 -msgid "Yes, enable notes in the source code AND show debugging details (not recommended for production)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:218 -msgid "No, I don't want my source code to contain any of these notes." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:229 -msgid "Plugin Deletion Safeguards" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:234 -msgid "Uninstall on Plugin Deletion; or Safeguard Options?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:235 -msgid "Tip: By default, if you delete %1$s using the plugins menu in WordPress, nothing is lost. However, if you want to completely uninstall %1$s you should set this to Yes and THEN deactivate & delete %1$s from the plugins menu in WordPress. This way %1$s will erase your options for the plugin, erase directories/files created by the plugin, remove the advanced-cache.php file, terminate CRON jobs, etc. It erases itself from existence completely." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:237 -msgid "Safeguard my options and the cache (recommended)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:238 -msgid "Yes, uninstall (completely erase) %1$s on plugin deletion." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:247 -msgid "Advanced Configuration (All Optional)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:248 -msgid "Recommended for advanced site owners only; already pre-configured for most WP installs." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:257 -msgid "Manual Cache Clearing" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:261 -msgid "Clearing the Cache Manually" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:263 -msgid "Once %1$s is enabled, you will find this new option in your WordPress Admin Bar (screenshot on right). Clicking this button will clear the cache and you can start fresh at anytime (e.g., you can do this manually; and as often as you wish)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:264 -msgid "Depending on the structure of your site, there could be many reasons to clear the cache. However, the most common reasons are related to Post/Page edits or deletions, Category/Tag edits or deletions, and Theme changes. %1$s handles most scenarios all by itself. However, many site owners like to clear the cache manually; for a variety of reasons (just to force a refresh)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:266 -msgid "Yes, enable "Clear Cache" button in admin bar" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:267 -msgid "No, I don't intend to clear the cache manually." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:270 -msgid "w/ dropdown options." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:271 -msgid "w/ dropdown options in split menu." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:272 -msgid "w/o dropdown options." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:277 -msgid "Also allow Child Sites in a Network to clear the cache from their Admin Bar?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:278 -msgid "In a Multisite Network, each child site can clear its own cache. If you want child sites to see the \"Clear Cache\" button in their WordPress Admin Bar, you can specify a comma-delimited list of Roles and/or Capabilities that are allowed. For example, if I want Administrators to be capable of clearing the cache from their Admin Bar, I could enter administrator here. If I also want to allow Editors, I can use a comma-delimited list: administrator,editor. Or, I could use a single Capability of: edit_others_posts; which covers both Administrators & Editors at the same time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:280 -#: src/includes/classes/MenuPageOptions.php:471 -msgid "Note: As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each child site owner must also have the ability to %1$s." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:285 -msgid "Also allow others to clear the cache from their Admin Bar?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:286 -msgid "If you want others to see the \"Clear Cache\" button in their WordPress Admin Bar, you can specify a comma-delimited list of Roles and/or Capabilities that are allowed. For example, if I want Editors to be capable of clearing the cache from their Admin Bar, I could enter editor here. If I also want to allow Authors, I can use a comma-delimited list: editor,author. Or, I could use a single Capability of: publish_posts; which covers both Editors & Authors at the same time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:288 -#: src/includes/classes/MenuPageOptions.php:479 -msgid "Note: As a security measure, in addition to the Role(s) and/or Capabilities that you list here, each user must also have the ability to %1$s." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:294 -msgid "Clear the PHP OPcache Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:295 -msgid "If you clear the cache manually, do you want %1$s to clear the PHP OPcache too? This is not necessary, but if you want a truly clean start, this will clear all PHP files in the server's opcode cache also. Note: If you don't already know what the PHP OPcache is, it is suggested that you leave this disabled. It really is not necessary. This is just an added feature for advanced users." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:297 -msgid "No, I don't use the PHP OPcache extension; or, I don't want the opcode cache cleared." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:298 -msgid "Yes, if the PHP OPcache extension is enabled, also clear the entire PHP opcode cache." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:303 -msgid "Clear the s2Clean Cache Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:304 -msgid "If the s2Clean theme is installed, and you clear the cache manually, %1$s can clear the s2Clean Markdown cache too (if you've enabled Markdown processing with s2Clean)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:306 -msgid "Yes, if the s2Clean theme is installed, also clear s2Clean-related caches." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:307 -msgid "No, I don't use s2Clean; or, I don't want s2Clean-related caches cleared." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:311 -msgid "Evaluate Custom PHP Code when Clearing the Cache?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:312 -msgid "If you have any custom routines you'd like to process when the cache is cleared manually, please enter PHP code here. If your PHP code outputs a message, it will be displayed along with any other notes from %1$s itself. This feature is intended for developers, and it may come in handy if you need to clear any system caches not already covered by %1$s configuration options." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:314 -msgid "Example: <?php apc_clear_cache(); echo '<p>Also cleared APC cache.</p>'; ?>" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:317 -msgid "Clear the CDN Cache Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:318 -msgid "If you clear the cache manually, do you want %1$s to automatically bump the CDN invalidation counter too? i.e., automatically increment the ?%2$s=[counter] in all static CDN URLs?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:320 -msgid "No, I don't use Static CDN Filters; or, I don't want the CDN cache cleared." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:321 -msgid "Yes, if Static CDN Filters are enabled, also clear the CDN cache." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:332 -msgid "Automatic Cache Clearing" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:337 -msgid "Clearing the Cache Automatically" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:339 -msgid "This is built into the %1$s plugin; i.e., this functionality is \"always on\". If you edit a Post/Page (or delete one), %1$s will automatically clear the cache file(s) associated with that content. This way a new updated version of the cache will be created automatically the next time this content is accessed. Simple updates like this occur each time you make changes in the Dashboard, and %1$s will notify you of these as they occur. %1$s monitors changes to Posts (of any kind, including Pages), Categories, Tags, Links, Themes (even Users), and more." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:343 -msgid "Yes, enable %1$s notifications in the Dashboard when changes are detected & one or more cache files are cleared automatically." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:344 -msgid "No, I don't want to know (don't really care) what %1$s is doing behind-the-scene." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:349 -msgid "Primary Page Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:351 -msgid "Auto-Clear Designated \"Home Page\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:352 -msgid "On many sites, the Home Page (aka: the Front Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the \"Home Page\"?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:354 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Home Page\"." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:355 -msgid "No, my Home Page does not provide a list of Posts/Pages; e.g., this is not necessary." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:357 -msgid "Auto-Clear Designated \"Posts Page\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:358 -msgid "On many sites, the Posts Page (aka: the Blog Page) offers an archive view of all Posts (or even Pages). Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the \"Posts Page\"?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:360 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Posts Page\"." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:361 -msgid "No, I don't use a separate Posts Page; e.g., my Home Page IS my Posts Page." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:365 -msgid "Author, Archive, and Tag/Term Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:367 -msgid "Auto-Clear \"Author Page\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:368 -msgid "On many sites, each author has a related \"Author Page\" that offers an archive view of all posts associated with that author. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the related \"Author Page\"?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:370 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the \"Author Page\"." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:371 -msgid "No, my site doesn't use multiple authors and/or I don't have any \"Author Page\" archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:374 -msgid "Auto-Clear \"Category Archives\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:375 -msgid "On many sites, each post is associated with at least one Category. Each category then has an archive view that contains all the posts within that category. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Category archive views?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:377 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the associated Category archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:378 -msgid "No, my site doesn't use Categories and/or I don't have any Category archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:381 -msgid "Auto-Clear \"Tag Archives\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:382 -msgid "On many sites, each post may be associated with at least one Tag. Each tag then has an archive view that contains all the posts assigned that tag. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:384 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the associated Tag archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:385 -msgid "No, my site doesn't use Tags and/or I don't have any Tag archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:388 -msgid "Auto-Clear \"Custom Term Archives\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:389 -msgid "Most sites do not use any custom Terms so it should be safe to leave this disabled. However, if your site uses custom Terms and they have their own Term archive views, you may want to clear those when the associated post is cleared. Therefore, if a single Post/Page is changed in some way; and %1$s clears/resets the cache for a single Post/Page, would you like %1$s to also clear any existing cache files for the associated Tag archive views?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:391 -msgid "Yes, if any single Post/Page is cleared/reset; also clear any associated custom Term archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:392 -msgid "No, my site doesn't use any custom Terms and/or I don't have any custom Term archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:395 -msgid "Auto-Clear \"Custom Post Type Archives\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:396 -msgid "Most sites do not use any Custom Post Types so it should be safe to disable this option. However, if your site uses Custom Post Types and they have their own Custom Post Type archive views, you may want to clear those when any associated post is cleared. Therefore, if a single Post with a Custom Post Type is changed in some way; and %1$s clears/resets the cache for that post, would you like %1$s to also clear any existing cache files for the associated Custom Post Type archive views?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:398 -msgid "Yes, if any single Post with a Custom Post Type is cleared/reset; also clear any associated Custom Post Type archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:399 -msgid "No, my site doesn't use any Custom Post Types and/or I don't have any Custom Post Type archive views." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:403 -msgid "Feed-Related Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:405 -msgid "Auto-Clear \"RSS/RDF/ATOM Feeds\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:406 -msgid "If you enable Feed Caching (below), this can be quite handy. If enabled, when you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that certain types of Feeds should be cleared to keep your site up-to-date, then %1$s will do this for you automatically. For instance, the blog's master feed, the blog's master comments feed, feeds associated with comments on a Post/Page, term-related feeds (including mixed term-related feeds), author-related feeds, etc. Under various circumstances (i.e., as you work in the Dashboard) these can be cleared automatically to keep your site up-to-date." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:408 -msgid "Yes, automatically clear RSS/RDF/ATOM Feeds from the cache when certain changes occur." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:409 -msgid "No, I don't have Feed Caching enabled, or I prefer not to automatically clear Feeds." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:413 -msgid "Sitemap-Related Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:415 -msgid "Auto-Clear \"XML Sitemaps\" Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:416 -msgid "If you're generating XML Sitemaps with a plugin like Google XML Sitemaps, you can tell %1$s to automatically clear the cache of any XML Sitemaps whenever it clears a Post/Page. Note: This does NOT clear the XML Sitemap itself of course, only the cache. The point being, to clear the cache and allow changes to a Post/Page to be reflected by a fresh copy of your XML Sitemap; sooner rather than later." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:418 -msgid "Yes, if any single Post/Page is cleared/reset; also clear the cache for any XML Sitemaps." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:419 -msgid "No, my site doesn't use any XML Sitemaps and/or I prefer NOT to clear the cache for XML Sitemaps." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:422 -msgid "XML Sitemap Patterns... A default value of /sitemap**.xml covers all XML Sitemaps for most installations. However, you may customize this further if you deem necessary. Please list one pattern per line. These XML Sitemap Pattern searches are performed against the REQUEST_URI. A wildcard ** character can also be used when necessary; e.g., /sitemap**.xml (where ** = 0 or more characters of any kind, including / slashes). Other special characters include: * = 0 or more characters that are NOT a slash /; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:425 -msgid "In a Multisite Network, each child blog (whether it be a sub-domain, a sub-directory, or a mapped domain); will automatically change the leading http://[sub.]domain/[sub-directory] used in pattern matching. In short, there is no need to add sub-domains or sub-directories for each child blog in these patterns. Please include only the REQUEST_URI (i.e., the path) which leads to the XML Sitemap on all child blogs in the network." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:431 -msgid "Misc. Auto-Clear Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:432 -msgid "Auto-Clear a List of Custom URLs Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:433 -msgid "When you update a Post/Page, approve a Comment, or make other changes where %1$s can detect that a Post/Page cache should be cleared to keep your site up-to-date; then %1$s will also clear a list of custom URLs that you list here. Please list one URL per line. A wildcard * character can also be used when necessary; e.g., /category/abc-followed-by-* (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:446 -msgid "Cache-Related Statistics" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:451 -msgid "Enable Cache-Related Stats & Charts?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:452 -msgid "%1$s can collect and display cache-related statistics (including charts). Stats are displayed in the WordPress Admin Bar, and also in your Dashboard under: %1$s → Stats/Charts. Cache-related stats provide you with a quick look at what's happening behind-the-scenes. Your site grows faster and faster as the cache grows larger in size." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:454 -msgid "Yes, enable stats collection & the menu page in WordPress for viewing stats." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:455 -msgid "No, I have a VERY large site and I want to avoid any unnecessary directory scans." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:457 -msgid "Note: %1$s does a great job of collecting stats, in ways that don't cause a performance issue. In addition, as your cache grows larger than several hundred files in total size, statistics are collected less often and at longer intervals. All of that being said, if you run a VERY large site (e.g., more than 20K posts), you might want to disable stats collection in favor of blazing fast speeds not impeded by any directory scans needed to collect stats." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:461 -msgid "Show Stats in the WordPress Admin Bar?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:463 -msgid "Yes, enable stats in the WordPress admin bar." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:464 -msgid "No, I'll review stats from the menu page in WordPress if I need to." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:468 -msgid "Allow Child Sites in a Network to See Stats in Admin Bar?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:469 -msgid "In a Multisite Network, each child site has stats of its own. If you want child sites to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of Roles and/or Capabilities that are allowed to see stats. For example, if I want the Administrator to see stats in their Admin Bar, I could enter administrator here. If I also want to show stats to Editors, I can use a comma-delimited list: administrator,editor. Or, I could use a single Capability of: edit_others_posts; which covers both Administrators & Editors at the same time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:476 -msgid "Allow Others to See Stats in Admin Bar?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:477 -msgid "If you want others to see cache-related stats in their WordPress Admin Bar, you can specify a comma-delimited list of Roles and/or Capabilities that are allowed to see stats. For example, if I want Editors to see stats in their Admin Bar, I could enter editor here. If I also want to show stats to Authors, I can use a comma-delimited list: editor,author. Or, I could use a single Capability of: publish_posts; which covers both Editors & Authors at the same time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:493 -msgid "Cache Directory" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:497 -msgid "Base Cache Directory (Must be Writable; i.e., Permissions 755 or Higher)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:498 -msgid "This is where %1$s will store the cached version of your site. If you're not sure how to deal with directory permissions, don't worry too much about this. If there is a problem, %1$s will let you know about it. By default, this directory is created by %1$s and the permissions are setup automatically. In most cases there is nothing more you need to do." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:509 -msgid "Cache Expiration Time" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:514 -msgid "Automatic Expiration Time (Max Age)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:515 -msgid "If you don't update your site much, you could set this to 6 months and optimize everything even further. The longer the Cache Expiration Time is, the greater your performance gain. Alternatively, the shorter the Expiration Time, the fresher everything will remain on your site. A default value of 7 days (recommended); is a good conservative middle-ground." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:516 -msgid "Keep in mind that your Expiration Time is only one part of the big picture. %1$s will also clear the cache automatically as changes are made to the site (i.e., you edit a post, someone comments on a post, you change your theme, you add a new navigation menu item, etc., etc.). Thus, your Expiration Time is really just a fallback; e.g., the maximum amount of time that a cache file could ever possibly live." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:517 -msgid "All of that being said, you could set this to just 60 seconds and you would still see huge differences in speed and performance. If you're just starting out with %1$s (perhaps a bit nervous about old cache files being served to your visitors); you could set this to something like 30 minutes and experiment with it while you build confidence in %1$s. It's not necessary to do so, but many site owners have reported this makes them feel like they're more-in-control when the cache has a short expiration time. All-in-all, it's a matter of preference ." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:519 -msgid "Tip: the value that you specify here MUST be compatible with PHP's strtotime() function. Examples: 30 seconds, 2 hours, 7 days, 6 months, 1 year." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:520 -msgid "Note: %1$s will never serve a cache file that is older than what you specify here (even if one exists in your cache directory; stale cache files are never used). In addition, a WP Cron job will automatically cleanup your cache directory (once per hour); purging expired cache files periodically. This prevents a HUGE cache from building up over time, creating a potential storage issue." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:524 -msgid "Cache Cleanup Schedule" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:525 -msgid "If you have an extremely large site and you lower the default Cache Expiration Time of 7 days, expired cache files can build up more quickly. By default, %1$s cleans up expired cache files via WP Cron at an hourly interval, but you can tell %1$s to use a custom Cache Cleanup Schedule below to run the cleanup process more or less frequently, depending on your specific needs." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:537 -msgid "Disable Cache Expiration If Server Load Average is High?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:538 -msgid "If you have high traffic at certain times of the day, %1$s can be told to check the current load average via sys_getloadavg(). If your server's load average has been high in the last 15 minutes or so, cache expiration is disabled automatically to help reduce stress on the server; i.e., to avoid generating a new version of the cache while the server is very busy." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:539 -msgid "To enable this functionality you should first determine what a high load average is for your server. If you log into your machine via SSH you can run the top command to get a feel for what a high load average looks like. Once you know the number, you can enter it in the field below; e.g., 1.05 might be a high load average for a server with one CPU. See also: Understanding Load Average" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:542 -msgid "Note: It appears that your server is running Windows. The sys_getloadavg() function has not been implemented in PHP for Windows servers yet." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:544 -msgid "Note: sys_getloadavg() has been disabled by your web hosting company or is not available on your server." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:557 -msgid "Client-Side Cache" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:562 -msgid "Allow Double-Caching In The Client-Side Browser?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:563 -msgid "Recommended setting: No (for membership sites, very important). Otherwise, Yes would be better (if users do NOT log in/out of your site)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:564 -msgid "%1$s handles content delivery through its ability to communicate with a browser using PHP. If you allow a browser to (cache) the caching system itself, you are momentarily losing some control; and this can have a negative impact on users that see more than one version of your site; e.g., one version while logged-in, and another while NOT logged-in. For instance, a user may log out of your site, but upon logging out they report seeing pages on the site which indicate they are STILL logged in (even though they're not — that's bad). This can happen if you allow a client-side cache, because their browser may cache web pages they visited while logged into your site which persist even after logging out. Sending no-cache headers will work to prevent this issue." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:565 -msgid "All of that being said, if all you care about is blazing fast speed and users don't log in/out of your site (only you do); you can safely set this to Yes (recommended in this case). Allowing a client-side browser cache will improve speed and reduce outgoing bandwidth when this option is feasible." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:567 -msgid "No, prevent a client-side browser cache (safest option)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:568 -msgid "Yes, I will allow a client-side browser cache of pages on the site." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:570 -msgid "Tip: Setting this to No is highly recommended when running a membership plugin like s2Member (as one example). In fact, many plugins like s2Member will send nocache_headers() on their own, so your configuration here will likely be overwritten when you run such plugins (which is better anyway). In short, if you run a membership plugin, you should NOT allow a client-side browser cache." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:571 -msgid "Tip: Setting this to No will NOT impact static content; e.g., CSS, JS, images, or other media. This setting pertains only to dynamic PHP scripts which produce content generated by WordPress." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:572 -msgid "Advanced Tip: if you have this set to No, but you DO want to allow a few special URLs to be cached by the browser; you can add this parameter to your URL ?%2$sABC=1. This tells %1$s that it's OK for the browser to cache that particular URL. In other words, the %2$sABC=1 parameter tells %1$s NOT to send no-cache headers to the browser." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:573 -msgid "Exclusion Patterns for Client-Side Caching" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:574 -msgid "When you enable Client-Side Caching above, you may want to prevent certain pages on your site from being cached by a client-side browser. This is where you will enter those if you need to (one per line). Searches are performed against the REQUEST_URI; i.e., /path/?query (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard * character can also be used when necessary; e.g., /category/abc-followed-by-* (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:576 -#: src/includes/classes/MenuPageOptions.php:700 -#: src/includes/classes/MenuPageOptions.php:861 -msgid "Tip: let's use this example URL: http://www.example.com/post/example-post-123. To exclude this URL, you would put this line into the field above: /post/example-post-123. Or, you could also just put in a small fragment, like: example or example-*-123 and that would exclude any URI containing that word fragment." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:577 -#: src/includes/classes/MenuPageOptions.php:701 -#: src/includes/classes/MenuPageOptions.php:719 -#: src/includes/classes/MenuPageOptions.php:737 -#: src/includes/classes/MenuPageOptions.php:853 -#: src/includes/classes/MenuPageOptions.php:857 -#: src/includes/classes/MenuPageOptions.php:862 -#: src/includes/classes/MenuPageOptions.php:986 -msgid "Note: please remember that your entries here should be formatted as a line-delimited list; e.g., one exclusion pattern per line." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:588 -msgid "Logged-In Users" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:593 -msgid "Caching Enabled for Logged-In Users & Comment Authors?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:594 -msgid "This should almost ALWAYS be set to No. Most sites will NOT want to cache content generated while a user is logged-in. Doing so could result in a cache of dynamic content generated specifically for a particular user, where the content being cached may contain details that pertain only to the user that was logged-in when the cache was generated. Imagine visiting a website that says you're logged-in as Billy Bob (but you're not Billy Bob; NOT good). In short, do NOT turn this on unless you know what you're doing." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:596 -msgid "Exception (Membership Sites): If you run a site with many users and the majority of your traffic comes from users who ARE logged-in, please choose: Yes (maintain separate cache). %1$s will operate normally; but when a user is logged-in, the cache is user-specific. %1$s will intelligently refresh the cache when/if a user submits a form on your site with the GET or POST method. Or, if you make changes to their account (or another plugin makes changes to their account); including user option|meta additions, updates & deletions too. However, please note that enabling this feature (e.g., user-specific cache entries); will eat up MUCH more disk space. That being said, the benefits of this feature for most sites will outweigh the disk overhead (e.g., it's NOT an issue in most cases). Unless you are short on disk space (or you have MANY thousands of users), the disk overhead is neglible." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:598 -msgid "No, do NOT cache; or serve a cache file when a user is logged-in (safest option)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:599 -msgid "Yes, and maintain a separate cache for each user (recommended for membership sites)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:602 -msgid "Yes, but DON'T maintain a separate cache for each user (I know what I'm doing)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:606 -msgid "Warning: Whenever you enable caching for logged-in users (without a separate cache for each user), the WordPress Admin Bar must be disabled to prevent one user from seeing another user's details in the Admin Bar. Given your current configuration, %1$s will automatically hide the WordPress Admin Bar on the front-end of your site." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:608 -msgid "Note: For most sites, the majority of their traffic (if not all of their traffic) comes from visitors who are not logged in, so disabling the cache for logged-in users is NOT ordinarily a performance issue. When a user IS logged-in, disabling the cache is considered ideal, because a logged-in user has a session open with your site; and the content they view should remain very dynamic in this scenario." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:609 -msgid "Note: This setting includes some users who AREN'T actually logged into the system, but who HAVE authored comments recently. %1$s includes comment authors as part of it's logged-in user check. This way comment authors will be able to see updates to the comment thread immediately; and, so that any dynamically-generated messages displayed by your theme will work as intended. In short, %1$s thinks of a comment author as a logged-in user, even though technically they are not. ~ Users who gain access to password-protected Posts/Pages are also included." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:611 -msgid "Static CDN Filters Enabled for Logged-In Users & Comment Authors?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:612 -msgid "While this defaults to a value of No, it should almost always be set to Yes. This value defaults to No only because Logged-In User caching (see above) defaults to No and setting this value to Yes by default can cause confusion for some users. Once you understand that Static CDN Filters can be applied safely for all visitors (logged-in or not logged-in), please choose Yes in the dropdown below. If you are not using Static CDN Filters, the value below is ignored." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:614 -msgid "No, disable Static CDN Filters when a user is logged-in." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:615 -msgid "Yes, enable Static CDN Filters for logged-in users (recommended) ." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:617 -msgid "Note: Static CDN Filters serve static resources. Static resources, are, simply put, static. Thus, it is not a problem to cache these resources for any visitor (logged-in or not logged-in). To avoid confusion, this defaults to a value of No, and we ask that you set it to Yes on your own so that you'll know to expect this behavior; i.e., that static resources will always be served from the CDN (logged-in or not logged-in) even though Logged-In User caching may be disabled above." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:627 -msgid "GET Requests" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:632 -msgid "Caching Enabled for GET (Query String) Requests?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:633 -msgid "This should almost ALWAYS be set to No. UNLESS, you're using unfriendly Permalinks. In other words, if all of your URLs contain a query string (e.g., /?key=value); you're using unfriendly Permalinks. Ideally, you would refrain from doing this; and instead, update your Permalink options immediately; which also optimizes your site for search engines. That being said, if you really want to use unfriendly Permalinks, and ONLY if you're using unfriendly Permalinks, you should set this to Yes; and don't worry too much, the sky won't fall on your head :-)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:635 -msgid "No, do NOT cache (or serve a cache file) when a query string is present." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:636 -msgid "Yes, I would like to cache URLs that contain a query string." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:638 -msgid "Note: POST requests (i.e., forms with method="post") are always excluded from the cache, which is the way it should be. Any POST/PUT/DELETE request should NEVER (ever) be cached. CLI (and self-serve) requests are also excluded from the cache (always). A CLI request is one that comes from the command line; commonly used by CRON jobs and other automated routines. A self-serve request is an HTTP connection established from your site -› to your site. For instance, a WP Cron job, or any other HTTP request that is spawned not by a user, but by the server itself." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:639 -msgid "Advanced Tip: If you are NOT caching GET requests (recommended), but you DO want to allow some special URLs that include query string parameters to be cached; you can add this special parameter to any URL ?%2$sAC=1. This tells %1$s that it's OK to cache that particular URL, even though it contains query string arguments. If you ARE caching GET requests and you want to force %1$s to NOT cache a specific request, you can add this special parameter to any URL ?%2$sAC=0." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:649 -msgid "404 Requests" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:654 -msgid "Caching Enabled for 404 Requests?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:655 -msgid "When this is set to No, %1$s will ignore all 404 requests and no cache file will be served. While this is fine for most site owners, caching the 404 page on a high-traffic site may further reduce server load. When this is set to Yes, %1$s will cache the 404 page (see Creating an Error 404 Page) and then serve that single cache file to all future 404 requests." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:657 -msgid "No, do NOT cache (or serve a cache file) for 404 requests." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:658 -msgid "Yes, I would like to cache the 404 page and serve the cached file for 404 requests." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:660 -msgid "How does %1$s cache 404 requests? %1$s will create a special cache file (----404----.html, see Advanced Tip below) for the first 404 request and then symlink future 404 requests to this special cache file. That way you don't end up with lots of 404 cache files that all contain the same thing (the contents of the 404 page). Instead, you'll have one 404 cache file and then several symlinks (i.e., references) to that 404 cache file." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:661 -msgid "Advanced Tip: The default 404 cache filename (----404----.html) is designed to minimize the chance of a collision with a cache file for a real page with the same name. However, if you want to override this default and define your own 404 cache filename, you can do so by adding define('COMET_CACHE_404_CACHE_FILENAME', 'your-404-cache-filename'); to your wp-config.php file (note that the .html extension should be excluded when defining a new filename)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:671 -msgid "RSS, RDF, and Atom Feeds" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:676 -msgid "Caching Enabled for RSS, RDF, Atom Feeds?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:677 -msgid "This should almost ALWAYS be set to No. UNLESS, you're sure that you want to cache your feeds. If you use a web feed management provider like Google® Feedburner and you set this option to Yes, you may experience delays in the detection of new posts. NOTE: If you do enable this, it is highly recommended that you also enable automatic Feed Clearing too. Please see the section above: \"Automatic Cache Clearing\". Find the sub-section titled: \"Auto-Clear RSS/RDF/ATOM Feeds\"." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:679 -msgid "No, do NOT cache (or serve a cache file) when displaying a feed." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:680 -msgid "Yes, I would like to cache feed URLs." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:682 -msgid "Note: This option affects all feeds served by WordPress, including the site feed, the site comment feed, post-specific comment feeds, author feeds, search feeds, and category and tag feeds. See also: WordPress Feeds." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:692 -msgid "URI Exclusion Patterns" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:696 -msgid "Don't Cache These Special URI Exclusion Patterns?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:697 -msgid "Sometimes there are certain cases where a particular file, or a particular group of files, should never be cached. This is where you will enter those if you need to (one per line). Searches are performed against the REQUEST_URI; i.e., /path/?query (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard * character can also be used when necessary; e.g., /category/abc-followed-by-* (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:711 -msgid "HTTP Referrer Exclusion Patterns" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:715 -msgid "Don't Cache These Special HTTP Referrer Exclusion Patterns?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:716 -msgid "Sometimes there are special cases where a particular referring URL (or referring domain) that sends you traffic; or even a particular group of referring URLs or domains that send you traffic; should result in a page being loaded on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the HTTP_REFERER (caSe insensitive). A wildcard * character can also be used when necessary; e.g., *.domain.com (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:718 -msgid "Tip: let's use this example URL: http://www.referring-domain.com/search/?q=search+terms. To exclude this referring URL, you could put this line into the field above: www.referring-domain.com. Or, you could also just put in a small fragment, like: /search/ or q=*; and that would exclude any referrer containing that word fragment." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:729 -msgid "User-Agent Exclusion Patterns" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:733 -msgid "Don't Cache These Special User-Agent Exclusion Patterns?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:734 -msgid "Sometimes there are special cases when a particular user-agent (e.g., a specific browser or a specific type of device); should be shown a page on your site that is NOT from the cache (and that resulting page should never be cached). This is where you will enter those if you need to (one per line). Searches are performed against the HTTP_USER_AGENT (caSe insensitive). A wildcard * character can also be used when necessary; e.g., Android *; Chrome/* Mobile (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:736 -msgid "Tip: if you wanted to exclude iPhones put this line into the field above: iPhone;*AppleWebKit. Or, you could also just put in a small fragment, like: iphone; and that would exclude any user-agent containing that word fragment. Note, this is just an example. With a default installation of %1$s, there is no compelling reason to exclude iOS devices (or any mobile device for that matter)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:748 -msgid "Auto-Cache Engine" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:753 -msgid "Enable the Auto-Cache Engine?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:754 -msgid "After using %1$s for awhile (or any other page caching plugin, for that matter); it becomes obvious that at some point (based on your configured Expiration Time) %1$s has to refresh itself. It does this by ditching its cached version of a page, reloading the database-driven content, and then recreating the cache with the latest data. This is a never ending regeneration cycle that is based entirely on your configured Expiration Time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:755 -msgid "Understanding this, you can see that 99% of your visitors are going to receive a lightning fast response from your server. However, there will always be around 1% of your visitors that land on a page for the very first time (before it's been cached), or land on a page that needs to have its cache regenerated, because the existing cache has become outdated. We refer to this as a First-Come Slow-Load Issue. Not a huge problem, but if you're optimizing your site for every ounce of speed possible, the Auto-Cache Engine can help with this. The Auto-Cache Engine has been designed to combat this issue by taking on the responsibility of being that first visitor to a page that has not yet been cached, or has an expired cache. The Auto-Cache Engine is powered, in part, by WP-Cron (already built into WordPress). The Auto-Cache Engine runs at 15-minute intervals via WP-Cron. It also uses the WP_Http class, which is also built into WordPress already." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:756 -msgid "The Auto-Cache Engine obtains its list of URLs to auto-cache, from two different sources. It can read an XML Sitemap and/or a list of specific URLs that you supply. If you supply both sources, it will use both sources collectively. The Auto-Cache Engine takes ALL of your other configuration options into consideration too, including your Expiration Time, as well as any cache exclusion rules." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:758 -msgid "No, leave the Auto-Cache Engine disabled please." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:759 -msgid "Yes, I want the Auto-Cache Engine to keep pages cached automatically." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:765 -msgid "XML Sitemap URL (or an XML Sitemap Index)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:769 -msgid "All URLs in this network are in the sitemap for the main site." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:770 -msgid "Using the path I've given, look for blog-specific sitemaps in each child blog also." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:772 -msgid " If enabled here, each child blog can be auto-cached too. %1$s will dynamically change the leading %2$s as necessary; for each child blog in the network. %1$s supports both sub-directory & sub-domain networks, including domain mapping plugins. For more information about how the Auto-Cache Engine caches child blogs, see this article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:776 -msgid "And/Or; a List of URLs to Auto-Cache (One Per Line)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:778 -msgid "Note: Wildcards are NOT supported here. If you are going to supply a list of URLs above, each line must contain one full URL for the Auto-Cache Engine to auto-cache. If you have many URLs, we recommend using an XML Sitemap." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:782 -msgid "Auto-Cache Delay Timer (in Milliseconds)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:783 -msgid "As the Auto-Cache Engine runs through each URL, you can tell it to wait X number of milliseconds between each connection that it makes. It is strongly suggested that you DO have some small delay here. Otherwise, you run the risk of hammering your own web server with multiple repeated connections whenever the Auto-Cache Engine is running. This is especially true on very large sites; where there is the potential for hundreds of repeated connections as the Auto-Cache Engine goes through a long list of URLs. Adding a delay between each connection will prevent the Auto-Cache Engine from placing a heavy load on the processor that powers your web server. A value of 500 milliseconds is suggested here (half a second). If you experience problems, you can bump this up a little at a time, in increments of 500 milliseconds; until you find a happy place for your server. Please note that 1000 milliseconds = 1 full second." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:788 -msgid "Auto-Cache User-Agent String" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:790 -msgid "This is how the Auto-Cache Engine identifies itself when connecting to URLs. See User Agent in the Wikipedia." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:802 -msgid "HTML Compression" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:807 -msgid "Enable WebSharks™ HTML Compression?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:809 -msgid "No, do NOT compress HTML/CSS/JS code at runtime." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:810 -msgid "Yes, I want to compress HTML/CSS/JS for blazing fast speeds." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:812 -msgid "Note: This is experimental. Please report issues here." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:815 -msgid "HTML Compression Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:816 -msgid "You can learn more about all of these options here." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:818 -msgid "Yes, combine CSS from <head> and <body> into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:819 -msgid "No, do not combine CSS from <head> and <body> into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:822 -msgid "Yes, compress the code in any unified CSS files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:823 -msgid "No, do not compress the code in any unified CSS files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:826 -msgid "Yes, combine JS from <head> into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:827 -msgid "No, do not combine JS from <head> into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:830 -msgid "Yes, combine JS footer scripts into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:831 -msgid "No, do not combine JS footer scripts into fewer files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:834 -msgid "Yes, combine CSS/JS from remote resources too." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:835 -msgid "No, do not combine CSS/JS from remote resources." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:838 -msgid "Yes, compress the code in any unified JS files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:839 -msgid "No, do not compress the code in any unified JS files." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:842 -msgid "Yes, compress inline JavaScript snippets." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:843 -msgid "No, do not compress inline JavaScript snippets." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:846 -msgid "Yes, compress (remove extra whitespace) in the final HTML code too." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:847 -msgid "No, do not compress the final HTML code." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:850 -msgid "CSS Exclusion Patterns?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:851 -msgid "Sometimes there are special cases when a particular CSS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <link href=""> value, and also against the contents of any inline <style> tags (caSe insensitive). A wildcard * character can also be used when necessary; e.g., xy*-framework (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:854 -msgid "JavaScript Exclusion Patterns?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:855 -msgid "Sometimes there are special cases when a particular JS file should NOT be consolidated or compressed in any way. This is where you will enter those if you need to (one per line). Searches are performed against the <script src=""> value, and also against the contents of any inline <script> tags (caSe insensitive). A wildcard * character can also be used when necessary; e.g., xy*-framework (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:858 -msgid "URI Exclusions for HTML Compressor?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:859 -msgid "When you enable HTML Compression above, you may want to prevent certain pages on your site from being cached by the HTML Compressor. This is where you will enter those if you need to (one per line). Searches are performed against the REQUEST_URI; i.e., /path/?query (caSe insensitive). So, don't put in full URLs here, just word fragments found in the file path (or query string) is all you need, excluding the http:// and domain name. A wildcard * character can also be used when necessary; e.g., /category/abc-followed-by-* (where * = 0 or more characters that are NOT a slash /). Other special characters include: ** = 0 or more characters of any kind, including / slashes; ^ = beginning of the string; $ = end of the string. To learn more about this syntax, please see this KB article." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:864 -msgid "HTML Compression Cache Expiration" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:866 -msgid "Tip: the value that you specify here MUST be compatible with PHP's strtotime() function. Examples: 2 hours, 7 days, 6 months, 1 year." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:867 -msgid "Note: This does NOT impact the overall cache expiration time that you configure with %1$s. It only impacts the sub-routines provided by the HTML Compressor. In fact, this expiration time is mostly irrelevant. The HTML Compressor uses an internal checksum, and it also checks filemtime() before using an existing cache file. The HTML Compressor class also handles the automatic cleanup of your cache directories to keep it from growing too large over time. Therefore, unless you have VERY little disk space there is no reason to set this to a lower value (even if your site changes dynamically quite often). If anything, you might like to increase this value which could help to further reduce server load. You can learn more here. We recommend setting this value to at least double that of your overall %1$s expiration time." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:869 -msgid "Enable HTML Compression for Logged-In Users?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:870 -msgid "Disabled by default. This setting is only applicable when caching for Logged-In Users is enabled. This should remain disabled for logged-in users because the user-specific cache has a much shorter Time To Live (TTL) which means their cache is likely to expire more quickly than a normal visitor. Rebuilding the HTML Compressor cache is time-consuming and doing it too frequently will actually slow things down for them. For example, if you're logged into the site as a user and you submit a form, that triggers a clearing of the cache for that user, including the HTML Compressor cache (when Logged-In User caching is enabled). Lots of little actions you take can result in a clearing of the cache. This shorter TTL is not ideal when running the HTML Compressor because it does a deep analysis of the page content and the associated resources in order to intelligently compress things. For logged-in users, it is better to skip that extra work and just cache the HTML source as-is, avoiding that extra overhead. In short, do NOT turn this on unless you know what you're doing." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:872 -msgid "No, disable HTML Compression for logged-in users (recommended)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:873 -msgid "Yes, enable HTML Compression for logged-in users." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:885 -msgid "GZIP Compression" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:890 -msgid "GZIP Compression (Optional; Highly Recommended)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:891 -msgid "You don't have to use an .htaccess file to enjoy the performance enhancements provided by this plugin; caching is handled automatically by WordPress/PHP alone. That being said, if you want to take advantage of the additional speed enhancements associated w/ GZIP compression (and we do recommend this), then you WILL need an .htaccess file to accomplish that part." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:892 -msgid "%1$s fully supports GZIP compression on its output. However, it does not handle GZIP compression directly. We purposely left GZIP compression out of this plugin, because GZIP compression is something that should really be enabled at the Apache level or inside your php.ini file. GZIP compression can be used for things like JavaScript and CSS files as well, so why bother turning it on for only WordPress-generated pages when you can enable GZIP at the server level and cover all the bases!" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:893 -msgid "If you want to enable GZIP, create an .htaccess file in your WordPress® installation directory, and put the following few lines in it. Alternatively, if you already have an .htaccess file, just add these lines to it, and that is all there is to it. GZIP is now enabled in the recommended way! See also: video about GZIP Compression." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:907 -msgid "Static CDN Filters" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:911 -msgid "Clear CDN Cache (Bump CDN Invalidation Counter)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:911 -msgid "Clear CDN Cache" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:912 -msgid "Enable Static CDN Filters (e.g., MaxCDN/CloudFront)?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:913 -msgid "This feature allows you to serve some and/or ALL static files on your site from a CDN of your choosing. This is made possible through content/URL filters exposed by WordPress and implemented by %1$s. All it requires is that you setup a CDN host name sourced by your WordPress installation domain. You enter that CDN host name below and %1$s will do the rest! Super easy, and it doesn't require any DNS changes either. :-) Please click here for a general set of instructions." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:914 -msgid "What's a CDN? It's a Content Delivery Network (i.e., a network of optimized servers) designed to cache static resources served from your site (e.g., JS/CSS/images and other static files) onto it's own servers, which are located strategically in various geographic areas around the world. Integrating a CDN for static files can dramatically improve the speed and performance of your site, lower the burden on your own server, and reduce latency associated with visitors attempting to access your site from geographic areas of the world that might be very far away from the primary location of your own web servers." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:917 -msgid "It appears that your server is running NGINX and does not support .htaccess rules. Please update your server configuration manually. Note that updating your NGINX server configuration before enabling Static CDN Filters is recommended to prevent any CORS errors with your CDN. If you've already updated your NGINX configuration, you can safely ignore this message." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:921 -msgid "No, I do NOT want CDN filters applied at runtime." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:922 -msgid "Yes, I want CDN filters applied w/ my configuration below." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:929 -msgid "CDN Host Name (Required)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:935 -msgid "This field is really all that's necessary to get Static CDN Filters working! However, it does requires a little bit of work on your part. You need to setup and configure a CDN before you can fill in this field. Once you configure a CDN, you'll receive a host name (provided by your CDN), which you'll enter here; e.g., js9dgjsl4llqpp.cloudfront.net. We recommend MaxCDN, Amazon CloudFront, KeyCDN, and/or CDN77 but this should work with many of the most popular CDNs. Please read this article for a general set of instructions. We also have a MaxCDN tutorial, CloudFront tutorial, KeyCDN tutorial, and a CDN77 tutorial to walk you through the process." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:940 -msgid "Multiple CDN Host Names for Domain Sharding and Multisite Networks (Optional)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:941 -msgid "%1$s also supports multiple CDN Host Names for any given domain. Using multiple CDN Host Names (instead of just one, as seen above) is referred to as Domain Sharding (click here to learn more). If you configure multiple CDN Host Names (i.e., if you implement Domain Sharding), %1$s will use the first one that you list for static resources loaded in the HTML <head> section, the last one for static resources loaded in the footer, and it will choose one at random for all other static resource locations. Configuring multiple CDN Host Names can improve speed! This is a way for advanced site owners to work around concurrency limits in popular browsers; i.e., making it possible for browsers to download many more resources simultaneously, resulting in a faster overall completion time. In short, this tells the browser that your website will not be overloaded by concurrent requests, because static resources are in fact being served by a content-delivery network (i.e., multiple CDN host names). If you use this functionality for Domain Sharding, we suggest that you setup one CDN Distribution (aka: Pull Zone), and then create multiple CNAME records pointing to that distribution. You can enter each of your CNAMES in the field below, as instructed." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:942 -msgid "On WordPress Multisite Network installations, this field also allows you to configure different CDN Host Names for each domain (or sub-domain) that you run from a single installation of WordPress. For more information about configuring Static CDN Filters on a WordPress Multisite Network, see this tutorial: Static CDN Filters for WordPress Multisite Networks." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:944 -msgid "↑ Syntax: This is a line-delimited list of domain mappings. Each line should start with your WordPress domain name (e.g., %1$s), followed by an = sign, followed by a comma-delimited list of CDN Host Names associated with the domain in that line. If you're running a Multisite Network installation of WordPress, you might have multiple configuration lines. Otherwise, you should only need one line to configure multiple CDN Host Names for a standard WordPress installation." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:948 -msgid "CDN Supports HTTPS Connections?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:950 -msgid "No, I don't serve content over https://; or I haven't configured my CDN w/ an SSL certificate." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:951 -msgid "Yes, I've configured my CDN w/ an SSL certificate; I need https:// enabled." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:958 -msgid "Additional Options (For Advanced Users)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:963 -msgid "Everything else below is 100% completely optional; i.e., not required to enjoy the benefits of Static CDN Filters." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:967 -msgid "Whitelisted File Extensions (Optional; Comma-Delimited)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:969 -msgid "If you leave this empty a default set of extensions are taken from WordPress itself. The default set of whitelisted file extensions includes everything supported by the WordPress media library." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:971 -msgid "Blacklisted File Extensions (Optional; Comma-Delimited)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:973 -msgid "With or without a whitelist, you can force exclusions by explicitly blacklisting certain file extensions of your choosing. Please note, the php extension will never be considered a static resource; i.e., it is automatically blacklisted at all times." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:977 -msgid "Whitelisted URI Inclusion Patterns (Optional; One Per Line)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:979 -msgid "Note: please remember that your entries here should be formatted as a line-delimited list; e.g., one inclusion pattern per line." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:980 -msgid "If provided, only local URIs matching one of the patterns you list here will be served from your CDN Host Name. URI patterns are caSe-insensitive. A wildcard * will match zero or more characters in any of your patterns. A caret ^ symbol will match zero or more characters that are NOT the / character. For instance, */wp-content/* here would indicate that you only want to filter URLs that lead to files located inside the wp-content directory. Adding an additional line with */wp-includes/* would filter URLs in the wp-includes directory also. If you leave this empty, ALL files matching a static file extension will be served from your CDN; i.e., the default behavior." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:981 -msgid "Please note that URI patterns are tested against a file's path (i.e., a file's URI, and NOT its full URL). A URI always starts with a leading /. To clarify, a URI is the portion of the URL which comes after the host name. For instance, given the following URL: http://example.com/path/to/style.css?ver=3, the URI you are matching against would be: /path/to/style.css?ver=3. To whitelist this URI, you could use a line that contains something like this: /path/to/*.css*" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:983 -msgid "Blacklisted URI Exclusion Patterns (Optional; One Per Line)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:985 -msgid "With or without a whitelist, you can force exclusions by explicitly blacklisting certain URI patterns. URI patterns are caSe-insensitive. A wildcard * will match zero or more characters in any of your patterns. A caret ^ symbol will match zero or more characters that are NOT the / character. For instance, */wp-content/*/dynamic.pdf* would exclude a file with the name dynamic.pdf located anywhere inside a sub-directory of wp-content." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:990 -msgid "Query String Invalidation Variable Name" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:992 -msgid "Each filtered URL (which then leads to your CDN) will include this query string variable as an easy way to invalidate the CDN cache at any time. Invalidating the CDN cache is simply a matter of changing the global invalidation counter (i.e., the value assigned to this query string variable). %1$s manages invalidations automatically; i.e., %1$s will automatically bump an internal counter each time you upgrade a WordPress component (e.g., a plugin, theme, or WP itself). Or, if you ask %1$s to invalidate the CDN cache (e.g., a manual clearing of the CDN cache); the internal counter is bumped then too. In short, %1$s handles cache invalidations for you reliably. This option simply allows you to customize the query string variable name which makes cache invalidations possible. Please note, the default value is adequate for most sites. You can change this if you like, but it's not necessary." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:993 -msgid "Tip: You can also tell %1$s to automatically bump the CDN Invalidation Counter whenever you clear the cache manually. See: %1$s → Manual Cache Clearing → Clear the CDN Cache Too?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:994 -msgid "Note: If you empty this field, it will effectively disable the %1$s invalidation system for Static CDN Filters; i.e., the query string variable will NOT be included if you do not supply a variable name." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1007 -msgid "Dynamic Version Salt" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1012 -msgid " GEEK ALERT This is for VERY advanced users only..." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1013 -msgid "Note: Understanding the %1$s Branched Cache Structure is a prerequisite to understanding how Dynamic Version Salts are added to the mix." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1014 -msgid "A Version Salt gives you the ability to dynamically create multiple variations of the cache, and those dynamic variations will be served on subsequent visits; e.g., if a visitor has a specific cookie (of a certain value) they will see pages which were cached with that version (i.e., w/ that Version Salt: the value of the cookie). A Version Salt can really be anything." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1015 -msgid "A Version Salt can be a single variable like $_COOKIE['my_cookie'], or it can be a combination of multiple variables, like $_COOKIE['my_cookie'].$_COOKIE['my_other_cookie']. (When using multiple variables, please separate them with a dot, as shown in the example.)" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1016 -msgid "Experts could even use PHP ternary expressions that evaluate into something. For example: ((preg_match('/iPhone/i', $_SERVER['HTTP_USER_AGENT'])) ? 'iPhones' : ''). This would force a separate version of the cache to be created for iPhones (e.g., /cache/PROTOCOL/HOST/REQUEST-URI.v/iPhones.html)." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1017 -msgid "For more documentation, please see Dynamic Version Salts." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1019 -msgid "Create a Dynamic Version Salt For %1$s?       150%% OPTIONAL" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1021 -msgid "Super Globals work here; $GLOBALS['table_prefix'] is a popular one.
Or, perhaps a PHP Constant defined in /wp-config.php; such as WPLANG or DB_HOST." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1022 -msgid "Important: your Version Salt is scanned for PHP syntax errors via phpCodeChecker.com. If errors are found, you'll receive a notice in the Dashboard." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1023 -msgid "If you've enabled a separate cache for each user (optional) that's perfectly OK. A Version Salt works with user caching too." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1033 -msgid "Theme/Plugin Developers" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1038 -msgid "Developing a Theme or Plugin for WordPress?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1039 -msgid "Tip: %1$s can be disabled temporarily. If you're a theme/plugin developer, you can set a flag within your PHP code to disable the cache engine at runtime. Perhaps on a specific page, or in a specific scenario. In your PHP script, set: $_SERVER['COMET_CACHE_ALLOWED'] = FALSE; or define('COMET_CACHE_ALLOWED', FALSE). %1$s is also compatible with: define('DONOTCACHEPAGE', TRUE). It does't matter where or when you define one of these, because %1$s is the last thing to run before script execution ends." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1041 -msgid "Writing \"Advanced Cache\" Plugins Specifically for %1$s" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1042 -msgid "Theme/plugin developers can take advantage of the %1$s plugin architecture by creating PHP files inside this special directory: /wp-content/ac-plugins/. There is an example plugin file @ GitHub (please review it carefully and ask questions). If you develop a plugin for %1$s, please share it with the community by publishing it in the plugins respository at WordPress.org." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1043 -msgid "Why does %1$s have it's own plugin architecture? WordPress loads the advanced-cache.php drop-in file (for caching purposes) very early-on; before any other plugins or a theme. For this reason, %1$s implements it's own watered-down version of functions like add_action(), do_action(), add_filter(), apply_filters()." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1054 -msgid "Import/Export Options" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1059 -msgid "Import Options from Another %1$s Installation?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1060 -msgid "Upload your %1$s-options.json file and click \"Save All Changes\" below. The options provided by your import file will override any that exist currently." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1063 -msgid "Export Existing Options from this %1$s Installation?" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1066 -msgid "%1$s-options.json" -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1067 -msgid "Download your existing options and import them all into another %1$s installation; saves time on future installs." -msgstr "" - -#: src/includes/classes/MenuPageOptions.php:1075 -msgid "Save All Changes" -msgstr "" - -#: src/includes/classes/VsUpgrades.php:201 -msgid "Woohoo! %1$s activated. :-)" -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:75 -msgid "because `PHP_SAPI` reports that you are currently running from the command line." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:79 -msgid "because `$_SERVER['HTTP_HOST']` is missing from your server configuration." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:83 -msgid "because `$_SERVER['REQUEST_URI']` is missing from your server configuration." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:88 -msgid "because the s2Member plugin set the PHP constant `COMET_CACHE_ALLOWED` to a boolean-ish `FALSE` value at runtime. The s2Member plugin is serving content that must remain dynamic on this particular page, and therefore this page was intentionally not cached for a very good reason." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:90 -msgid "because the PHP constant `COMET_CACHE_ALLOWED` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:95 -msgid "because the environment variable `$_SERVER['COMET_CACHE_ALLOWED']` has been set to a boolean-ish `FALSE` value at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:99 -msgid "because the PHP constant `DONOTCACHEPAGE` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:103 -msgid "because the environment variable `$_SERVER['DONOTCACHEPAGE']` has been set at runtime. Perhaps by WordPress itself, or by one of your themes/plugins. This usually means that you have a theme/plugin intentionally disabling the cache on this page; and it's usually for a very good reason." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:107 -msgid "because `$_GET['%1$sAC']` is set to a boolean-ish FALSE value." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:111 -msgid "because `$_SERVER['REQUEST_METHOD']` is `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`. These request methods should never (ever) be cached in any way." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:115 -msgid "because `[current IP address]` === `$_SERVER['SERVER_ADDR']`; i.e. a self-serve request. DEVELOPER TIP: if you are testing on a localhost installation, please add `define('LOCALHOST', TRUE);` to your `/wp-config.php` file while you run tests :-) Remove it (or set it to a `FALSE` value) once you go live on the web." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:119 -msgid "because `$_SERVER['REQUEST_URI']` indicates this is a `/feed`; and the configuration of this site says not to cache XML-based feeds." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:123 -msgid "because `$_SERVER['REQUEST_URI']` indicates this is a `wp-` or `xmlrpc` file; i.e. a WordPress systematic file. WordPress systematics are never (ever) cached in any way." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:127 -msgid "because `$_SERVER['REQUEST_URI']` or the `is_admin()` function indicates this is an administrative area of the site." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:131 -msgid "because `$_SERVER['REQUEST_URI']` indicates this is a Multisite Network; and this was a request for `/files/*`, not a page." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:136 -msgid "because the current user visiting this page (usually YOU), appears to be logged-in. The current configuration says NOT to cache pages for logged-in visitors. This message may also appear if you have an active PHP session on this site, or if you've left (or replied to) a comment recently. If this message continues, please clear your cookies and try again." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:140 -msgid "because the current page contains `_wpnonce` or `akismet_comment_nonce`. While your current configuration states that pages SHOULD be cache for logged-in visitors, `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:144 -msgid "because the current page contains `_wpnonce` or `akismet_comment_nonce`. Note that `*nonce*` values in the markup are not cache-compatible. See http://wsharks.com/1O1Kudy for further details." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:148 -msgid "because the current user appeared to be logged into the site (in one way or another); but %1$s was unable to formulate a User Token for them. Please report this as a possible bug." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:152 -msgid "because `$_GET` contains query string data. The current configuration says NOT to cache GET requests with a query string." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:156 -msgid "because `$_REQUEST` indicates this is simply a preview of something to come." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:160 -msgid "because `$_SERVER['REQUEST_URI']` matches a configured URI Exclusion Pattern on this installation." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:164 -msgid "because `$_SERVER['HTTP_USER_AGENT']` matches a configured User-Agent Exclusion Pattern on this installation." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:168 -msgid "because `$_SERVER['HTTP_REFERER']` and/or `$_GET['_wp_http_referer']` matches a configured HTTP Referrer Exclusion Pattern on this installation." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:172 -msgid "because the WordPress `is_404()` Conditional Tag says the current page is a 404 error. The current configuration says NOT to cache 404 errors." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:176 -msgid "because a plugin running on this installation says this page is in Maintenance Mode; i.e. is not available publicly at this time." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:180 -msgid "because %1$s is unable to cache already-compressed output. Please use `mod_deflate` w/ Apache; or use `zlib.output_compression` in your `php.ini` file. %1$s is NOT compatible with `ob_gzhandler()` and others like this." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:184 -msgid "because the contents of this document contain ``, which indicates this is an auto-generated WordPress error message." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:188 -msgid "because a `Content-Type:` header was set via PHP at runtime. The header contains a MIME type which is NOT a variation of HTML or XML. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:192 -msgid "because a `Status:` header (or an `HTTP/` header) was set via PHP at runtime. The header contains a non-`2xx` status code. This indicates the current page was not loaded successfully. This header might have been set by your hosting company, by WordPress itself; or by one of your themes/plugins." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:196 -msgid "because the WordPress `is_404()` Conditional Tag says the current page is a 404 error; and this is the first time it's happened on this page. Your current configuration says that 404 errors SHOULD be cached, so %1$s built a cached symlink which points future requests for this location to your already-cached 404 error document. If you reload this page (assuming you don't clear the cache before you do so); you should get a cached version of your 404 error document. This message occurs ONCE for each new/unique 404 error request." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:200 -msgid "because %1$s detected an early output buffer termination. This may happen when a theme/plugin ends, cleans, or flushes all output buffers before reaching the PHP shutdown phase. It's not always a bad thing. Sometimes it is necessary for a theme/plugin to do this. However, in this scenario it is NOT possible to cache the output; since %1$s is effectively disabled at runtime when this occurs." -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:204 -msgid "due to an unexpected behavior in the application. Please report this as a bug!" -msgstr "" - -#: src/includes/closures/Ac/NcDebugUtils.php:208 -msgid "%1$s is NOT caching this page, %2$s" -msgstr "" - -#. translators: This string is actually NOT translatable because the `__()` -#. function is not available at this point in the processing. -#: src/includes/closures/Ac/ObUtils.php:210 -msgid "%1$s fully functional :-) Cache file served for (%2$s) in %3$s seconds, on: %4$s." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:238 -msgid "Unexpected OB phase: `%1$s`." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:318 -msgid "Cache directory not writable. %1$s needs this directory please: `%2$s`. Set permissions to `755` or higher; `777` might be needed in some cases." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:324 -#: src/includes/closures/Ac/ObUtils.php:342 -msgid "Unable to create symlink: `%1$s` » `%2$s`. Possible permissions issue (or race condition), please check your cache directory: `%3$s`." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:335 -msgid "%1$s file path: %2$s" -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:336 -msgid "%1$s file built for (%2$s%3$s) in %4$s seconds, on: %5$s." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:336 -msgid "user token: %1$s" -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:337 -msgid "This %1$s file will auto-expire (and be rebuilt) on: %2$s (based on your configured expiration time)." -msgstr "" - -#: src/includes/closures/Ac/ObUtils.php:352 -msgid "%1$s: failed to write cache file for: `%2$s`; possible permissions issue (or race condition), please check your cache directory: `%3$s`." -msgstr "" - -#: src/includes/closures/Plugin/CronUtils.php:18 -msgid "Every 15 Minutes" -msgstr "" - -#: src/includes/closures/Plugin/DirUtils.php:21 -#: src/includes/closures/Plugin/DirUtils.php:48 -msgid "Missing `base_dir` option value." -msgstr "" - -#: src/includes/closures/Plugin/InstallUtils.php:16 -msgid "%1$s successfully installed! :-) Please enable caching and review options." -msgstr "" - -#: src/includes/closures/Plugin/InstallUtils.php:55 -msgid "%1$s: detected a new version of itself. Recompiling w/ latest version... wiping the cache... all done :-)" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:46 -msgid "%" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:47 -msgid "file" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:48 -msgid "files" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:49 -msgid "Page Cache" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:50 -msgid "HTML Compressor" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:51 -msgid "Current Total" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:52 -msgid "Current Site" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:53 -msgid "%s Day High" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:118 -msgid "Settings" -msgstr "" - -#: src/includes/closures/Plugin/MenuPageUtils.php:121 -msgid "Upgrade" -msgstr "" - -#: src/includes/closures/Plugin/NoticeUtils.php:151 -msgid "dismiss ×" -msgstr "" - -#: src/includes/closures/Plugin/WcpAuthorUtils.php:82 -msgid "%1$s: detected changes. Found %2$s in the cache for Author Page: %3$s; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpFeedUtils.php:113 -msgid "%1$s: detected changes. Found %2$s in the cache, for XML feeds of type: %3$s; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpHomeBlogUtils.php:38 -msgid "%1$s: detected changes. Found %2$s in the cache for the designated \"Home Page\"; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpHomeBlogUtils.php:96 -msgid "%1$s: detected changes. Found %2$s in the cache for the designated \"Posts Page\"; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpPostTypeUtils.php:51 -msgid "Untitled" -msgstr "" - -#: src/includes/closures/Plugin/WcpPostTypeUtils.php:61 -msgid "%1$s: detected changes. Found %2$s in the cache for Custom Post Type: %3$s; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpPostUtils.php:82 -msgid "Post" -msgstr "" - -#: src/includes/closures/Plugin/WcpPostUtils.php:89 -msgid "%1$s: detected changes. Found %2$s in the cache for %3$s ID: %4$s; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpSitemapUtils.php:44 -msgid "%1$s: detected changes. Found %2$s in the cache for XML sitemaps; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpTermUtils.php:130 -msgid "%1$s: detected changes. Found %2$s in the cache for %3$s: %4$s; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:164 -msgid "%1$s: detected significant changes. Found %2$s in the cache; auto-wiping." -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:210 -msgid "%1$s: detected important site changes. Found %2$s in the cache for this site; auto-clearing." -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:244 -msgid "%1$s: detected important site changes. Found %2$s in the cache for this site that were expired; auto-purging." -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:278 -msgid "%1$s: detected important site changes. Found %2$s in the cache that were expired; auto-purging." -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:298 -msgid "%1$s: detected significant changes that would normally trigger cache wiping routines. However, cache wiping routines have been disabled by a site administrator. [?]" -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:318 -msgid "%1$s: detected important site changes that would normally trigger cache clearing routines. However, cache clearing routines have been disabled by a site administrator. [?]" -msgstr "" - -#: src/includes/closures/Plugin/WcpUtils.php:338 -msgid "%1$s: detected important site changes that would normally trigger cache purging routines. However, cache purging routines have been disabled by a site administrator. [?]" -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:24 -msgid "Unable to determine cache directory location." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:121 -#: src/includes/closures/Shared/CacheDirUtils.php:277 -msgid "Invalid argument; isAdvancedCache!" -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:148 -msgid "Unable to delete files. Rename failure on directory: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:172 -#: src/includes/closures/Shared/CacheDirUtils.php:340 -#: src/includes/closures/Shared/CacheDirUtils.php:480 -msgid "Unable to delete symlink: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:187 -#: src/includes/closures/Shared/CacheDirUtils.php:355 -#: src/includes/closures/Shared/CacheDirUtils.php:490 -msgid "Unable to delete file: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:203 -#: src/includes/closures/Shared/CacheDirUtils.php:371 -#: src/includes/closures/Shared/CacheDirUtils.php:500 -msgid "Unable to delete dir: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:211 -#: src/includes/closures/Shared/CacheDirUtils.php:379 -#: src/includes/closures/Shared/CacheDirUtils.php:508 -#: src/includes/closures/Shared/CacheDirUtils.php:603 -msgid "Unexpected resource type: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:218 -#: src/includes/closures/Shared/CacheDirUtils.php:316 -#: src/includes/closures/Shared/CacheDirUtils.php:386 -msgid "Unable to delete files. Rename failure on tmp directory: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:274 -msgid "Invalid argument; host token empty!" -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:467 -#: src/includes/closures/Shared/CacheDirUtils.php:515 -msgid "Unable to delete all files/dirs. Rename failure on tmp directory: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:519 -msgid "Unable to delete directory: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:578 -msgid "Unable to erase symlink: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:587 -msgid "Unable to erase file: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:596 -msgid "Unable to erase dir: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheDirUtils.php:610 -msgid "Unable to erase directory: `%1$s`." -msgstr "" - -#: src/includes/closures/Shared/CacheLockUtils.php:25 -msgid "Unable to find the wp-config.php file." -msgstr "" - -#: src/includes/closures/Shared/CacheLockUtils.php:42 -msgid "No writable tmp directory." -msgstr "" - -#: src/includes/closures/Shared/CacheLockUtils.php:48 -msgid "Unable to obtain an exclusive lock." -msgstr "" - -#: src/includes/closures/Shared/HookUtils.php:40 -msgid "Invalid hook." -msgstr "" - -#: src/includes/closures/Shared/I18nUtils.php:15 -msgid "%1$s file" -msgid_plural "%1$s files" -msgstr[0] "" -msgstr[1] "" - -#: src/includes/closures/Shared/I18nUtils.php:29 -msgid "%1$s directory" -msgid_plural "%1$s directories" -msgstr[0] "" -msgstr[1] "" - -#: src/includes/closures/Shared/I18nUtils.php:43 -msgid "%1$s file/directory" -msgid_plural "%1$s files/directories" -msgstr[0] "" -msgstr[1] "" - -#: src/includes/closures/Shared/SysUtils.php:65 -msgid "%s%%" -msgstr "" - -#. Plugin Name of the plugin/theme -msgid "Comet Cache" -msgstr "" - -#. Plugin URI of the plugin/theme -msgid "http://cometcache.com/" -msgstr "" - -#. Description of the plugin/theme -msgid "Comet Cache is an advanced WordPress caching plugin inspired by simplicity." -msgstr "" - -#. Author of the plugin/theme -msgid "WebSharks, Inc." -msgstr "" - -#. Author URI of the plugin/theme -msgid "http://websharks-inc.com/" -msgstr "" diff --git a/src/includes/uninstall.php b/src/includes/uninstall.php index 63c118d..0ad4941 100644 --- a/src/includes/uninstall.php +++ b/src/includes/uninstall.php @@ -6,14 +6,16 @@ */ namespace WebSharks\CometCache; +use WebSharks\CometCache\Classes; + if (!defined('WPINC')) { exit('Do NOT access this file directly: '.basename(__FILE__)); } -require_once dirname(__FILE__).'/stub.php'; +require_once __DIR__.'/stub.php'; $GLOBALS[GLOBAL_NS.'_uninstalling'] = true; // Needs to be set before calling Conflicts class -if (!Conflicts::check()) { - $GLOBALS[GLOBAL_NS] = new Plugin(false); +if (!Classes\Conflicts::check()) { + $GLOBALS[GLOBAL_NS] = new Classes\Plugin(false); $GLOBALS[GLOBAL_NS]->uninstall(); } diff --git a/src/vendor/autoload.php b/src/vendor/autoload.php index b8fd0b8..da2a0d1 100644 --- a/src/vendor/autoload.php +++ b/src/vendor/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit33853609dfb2ae52d36861a4ab25a03b::getLoader(); +return ComposerAutoloaderInitaf99bf4520f39031a7a2f2d15b8d1ed0::getLoader(); diff --git a/src/vendor/composer/ClassLoader.php b/src/vendor/composer/ClassLoader.php index 5e1469e..ff6ecfb 100644 --- a/src/vendor/composer/ClassLoader.php +++ b/src/vendor/composer/ClassLoader.php @@ -13,9 +13,7 @@ namespace Composer\Autoload; /** - * ClassLoader implements a PSR-0 class loader - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. * * $loader = new \Composer\Autoload\ClassLoader(); * @@ -39,6 +37,8 @@ * * @author Fabien Potencier * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ */ class ClassLoader { @@ -147,7 +147,7 @@ public function add($prefix, $paths, $prepend = false) * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-0 base directories + * @param array|string $paths The PSR-4 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException diff --git a/src/vendor/composer/autoload_psr4.php b/src/vendor/composer/autoload_psr4.php index 85043a5..7258d2f 100644 --- a/src/vendor/composer/autoload_psr4.php +++ b/src/vendor/composer/autoload_psr4.php @@ -9,5 +9,7 @@ 'WebSharks\\JsMinifier\\' => array($vendorDir . '/websharks/js-minifier/src/includes/classes'), 'WebSharks\\HtmlCompressor\\' => array($vendorDir . '/websharks/html-compressor/src/includes/classes'), 'WebSharks\\CssMinifier\\' => array($vendorDir . '/websharks/css-minifier/src/includes/classes'), - 'WebSharks\\CometCache\\' => array($baseDir . '/src/includes/classes'), + 'WebSharks\\CometCache\\Traits\\' => array($baseDir . '/src/includes/traits'), + 'WebSharks\\CometCache\\Interfaces\\' => array($baseDir . '/src/includes/interfaces'), + 'WebSharks\\CometCache\\Classes\\' => array($baseDir . '/src/includes/classes'), ); diff --git a/src/vendor/composer/autoload_real.php b/src/vendor/composer/autoload_real.php index 4183e86..93f4017 100644 --- a/src/vendor/composer/autoload_real.php +++ b/src/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit33853609dfb2ae52d36861a4ab25a03b +class ComposerAutoloaderInitaf99bf4520f39031a7a2f2d15b8d1ed0 { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit33853609dfb2ae52d36861a4ab25a03b', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitaf99bf4520f39031a7a2f2d15b8d1ed0', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit33853609dfb2ae52d36861a4ab25a03b', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitaf99bf4520f39031a7a2f2d15b8d1ed0', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { @@ -43,8 +43,3 @@ public static function getLoader() return $loader; } } - -function composerRequire33853609dfb2ae52d36861a4ab25a03b($file) -{ - require $file; -} diff --git a/src/vendor/composer/installed.json b/src/vendor/composer/installed.json index ae6b4e4..51f9b02 100644 --- a/src/vendor/composer/installed.json +++ b/src/vendor/composer/installed.json @@ -1,25 +1,31 @@ [ { - "name": "websharks/wp-php-rv", - "version": "150511", - "version_normalized": "150511", + "name": "websharks/js-minifier", + "version": "dev-master", + "version_normalized": "9999999-dev", "source": { "type": "git", - "url": "https://github.com/websharks/wp-php-rv.git", - "reference": "ed0a58f3647e59c740560235da1e61904dca8d84" + "url": "https://github.com/websharks/js-minifier.git", + "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/websharks/wp-php-rv/zipball/ed0a58f3647e59c740560235da1e61904dca8d84", - "reference": "ed0a58f3647e59c740560235da1e61904dca8d84", + "url": "https://api.github.com/repos/websharks/js-minifier/zipball/3c1e99af6df9df8f691642fbb769a10e49bfba23", + "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23", "shasum": "" }, "require": { - "php": ">=5.2" + "ext-mbstring": "*", + "php": ">=5.3" }, - "time": "2015-05-10 10:30:04", + "time": "2015-05-11 20:53:04", "type": "library", "installation-source": "dist", + "autoload": { + "psr-4": { + "WebSharks\\JsMinifier\\": "src/includes/classes" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-3.0+" @@ -41,12 +47,13 @@ "role": "developer" } ], - "description": "Stub for WordPress themes/plugins that require PHP vX.x+.", - "homepage": "https://github.com/websharks/wp-php-rv", + "description": "Compresses JavaScript code.", + "homepage": "https://github.com/websharks/js-minifier", "keywords": [ - "php", - "websharks", - "wordpress" + "JS", + "compressor", + "javascript", + "websharks" ] }, { @@ -106,30 +113,34 @@ ] }, { - "name": "websharks/js-minifier", - "version": "dev-master", - "version_normalized": "9999999-dev", + "name": "websharks/html-compressor", + "version": "160118", + "version_normalized": "160118", "source": { "type": "git", - "url": "https://github.com/websharks/js-minifier.git", - "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23" + "url": "https://github.com/websharks/html-compressor.git", + "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/websharks/js-minifier/zipball/3c1e99af6df9df8f691642fbb769a10e49bfba23", - "reference": "3c1e99af6df9df8f691642fbb769a10e49bfba23", + "url": "https://api.github.com/repos/websharks/html-compressor/zipball/bd1ba2407b9c2c067549600a6ef1faa726c3c2d2", + "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2", "shasum": "" }, "require": { + "ext-curl": "*", "ext-mbstring": "*", - "php": ">=5.3" + "ext-openssl": "*", + "php": ">=5.3", + "websharks/css-minifier": "dev-master", + "websharks/js-minifier": "dev-master" }, - "time": "2015-05-11 20:53:04", + "time": "2016-01-18 03:17:20", "type": "library", "installation-source": "dist", "autoload": { "psr-4": { - "WebSharks\\JsMinifier\\": "src/includes/classes" + "WebSharks\\HtmlCompressor\\": "src/includes/classes" } }, "notification-url": "https://packagist.org/downloads/", @@ -153,45 +164,37 @@ "role": "developer" } ], - "description": "Compresses JavaScript code.", - "homepage": "https://github.com/websharks/js-minifier", + "description": "Combines & compresses CSS/JS/HTML code.", + "homepage": "https://github.com/websharks/html-compressor", "keywords": [ - "JS", "compressor", - "javascript", + "html", "websharks" ] }, { - "name": "websharks/html-compressor", - "version": "160118", - "version_normalized": "160118", + "name": "websharks/sharkicons", + "version": "160221", + "version_normalized": "160221", "source": { "type": "git", - "url": "https://github.com/websharks/html-compressor.git", - "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2" + "url": "https://github.com/websharks/sharkicons.git", + "reference": "2e6d6663c050f343fe944b424dea1759aa67cd05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/websharks/html-compressor/zipball/bd1ba2407b9c2c067549600a6ef1faa726c3c2d2", - "reference": "bd1ba2407b9c2c067549600a6ef1faa726c3c2d2", + "url": "https://api.github.com/repos/websharks/sharkicons/zipball/2e6d6663c050f343fe944b424dea1759aa67cd05", + "reference": "2e6d6663c050f343fe944b424dea1759aa67cd05", "shasum": "" }, - "require": { - "ext-curl": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "php": ">=5.3", - "websharks/css-minifier": "dev-master", - "websharks/js-minifier": "dev-master" + "require-dev": { + "package/bourbon": "4.2.3" }, - "time": "2016-01-18 03:17:20", + "time": "2016-02-21 15:16:12", "type": "library", "installation-source": "dist", "autoload": { - "psr-4": { - "WebSharks\\HtmlCompressor\\": "src/includes/classes" - } + "psr-4": [] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -214,38 +217,35 @@ "role": "developer" } ], - "description": "Combines & compresses CSS/JS/HTML code.", - "homepage": "https://github.com/websharks/html-compressor", + "description": "Font containing WebSharks logos/icons.", + "homepage": "https://github.com/websharks/sharkicons", "keywords": [ - "compressor", - "html", - "websharks" + "php", + "websharks", + "wordpress" ] }, { - "name": "websharks/sharkicons", - "version": "160221", - "version_normalized": "160221", + "name": "websharks/wp-php-rv", + "version": "150511", + "version_normalized": "150511", "source": { "type": "git", - "url": "https://github.com/websharks/sharkicons.git", - "reference": "2e6d6663c050f343fe944b424dea1759aa67cd05" + "url": "https://github.com/websharks/wp-php-rv.git", + "reference": "ed0a58f3647e59c740560235da1e61904dca8d84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/websharks/sharkicons/zipball/2e6d6663c050f343fe944b424dea1759aa67cd05", - "reference": "2e6d6663c050f343fe944b424dea1759aa67cd05", + "url": "https://api.github.com/repos/websharks/wp-php-rv/zipball/ed0a58f3647e59c740560235da1e61904dca8d84", + "reference": "ed0a58f3647e59c740560235da1e61904dca8d84", "shasum": "" }, - "require-dev": { - "package/bourbon": "4.2.3" + "require": { + "php": ">=5.2" }, - "time": "2016-02-21 15:16:12", + "time": "2015-05-10 10:30:04", "type": "library", "installation-source": "dist", - "autoload": { - "psr-4": [] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-3.0+" @@ -267,8 +267,8 @@ "role": "developer" } ], - "description": "Font containing WebSharks logos/icons.", - "homepage": "https://github.com/websharks/sharkicons", + "description": "Stub for WordPress themes/plugins that require PHP vX.x+.", + "homepage": "https://github.com/websharks/wp-php-rv", "keywords": [ "php", "websharks", diff --git a/src/vendor/websharks/sharkicons/src/long-classes.min.css b/src/vendor/websharks/sharkicons/src/long-classes.min.css index 6617c30..c2fc18f 100644 --- a/src/vendor/websharks/sharkicons/src/long-classes.min.css +++ b/src/vendor/websharks/sharkicons/src/long-classes.min.css @@ -1,3 +1,2 @@ @font-face{font-family:sharkicons;src:url("fonts/sharkicons.eot?v160221");src:url("fonts/sharkicons.eot?#iefix&v160221") format("embedded-opentype"),url("fonts/sharkicons.ttf?v160221") format("truetype"),url("fonts/sharkicons.woff?v160221") format("woff"),url("fonts/sharkicons.svg?v160221#sharkicons") format("svg");font-weight:normal;font-style:normal}.sharkicon::before{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none}.sharkicon-broom::before{content:""}.sharkicon-comment-mail-one::before{content:""}.sharkicon-comment-mail::before{content:""}.sharkicon-s2member::before{content:""}.sharkicon-websharks::before{content:""}.sharkicon-wp-kb-articles::before{content:""}.sharkicon-zencache-logo::before{content:""}.sharkicon-zencache::before{content:""}.sharkicon-wp-sharks::before{content:""}.sharkicon-wp-sharks-fin::before{content:""}.sharkicon-comet-cache::before{content:""}.sharkicon-comet-cache-logo::before{content:""}.sharkicon-comet-cache-comet::before{content:""}.sharkicon-feat-watch::before{content:""}.sharkicon-feat-server::before{content:""}.sharkicon-feat-layers::before{content:""}.sharkicon-feat-box::before{content:""}.sharkicon-feat-ellipsis::before{content:""}.sharkicon-typi-group::before{content:""}.sharkicon-enty-bookmark::before{content:""}.sharkicon-enty-bookmarks::before{content:""}.sharkicon-enty-open-book::before{content:""}.sharkicon-enty-archive::before{content:""}.sharkicon-enty-area-graph::before{content:""}.sharkicon-enty-bucket::before{content:""}.sharkicon-enty-colors::before{content:""}.sharkicon-enty-copy::before{content:""}.sharkicon-enty-drive::before{content:""}.sharkicon-enty-feather::before{content:""}.sharkicon-enty-gauge::before{content:""}.sharkicon-enty-hand::before{content:""}.sharkicon-enty-lab-flask::before{content:""}.sharkicon-enty-mask::before{content:""}.sharkicon-enty-medal::before{content:""}.sharkicon-enty-exclamation::before{content:""}.sharkicon-enty-palette::before{content:""}.sharkicon-enty-ruler::before{content:""}.sharkicon-enty-shop::before{content:""}.sharkicon-enty-basket::before{content:""}.sharkicon-enty-cart::before{content:""}.sharkicon-enty-traffic-cone::before{content:""}.sharkicon-enty-tree::before{content:""}.sharkicon-enty-trophy::before{content:""}.sharkicon-enty-v-card::before{content:""}.sharkicon-enty-google-hangouts::before{content:""}.sharkicon-eleg-line-graph::before{content:""}.sharkicon-eleg-male::before{content:""}.sharkicon-eleg-female::before{content:""}.sharkicon-eleg-atom::before{content:""}.sharkicon-broc-cart::before{content:""}.sharkicon-broc-crap::before{content:""}.sharkicon-broc-atom::before{content:""}.sharkicon-icom-headphones::before{content:""}.sharkicon-icom-barcode::before{content:""}.sharkicon-icom-user::before{content:""}.sharkicon-icom-users::before{content:""}.sharkicon-icom-user-plus::before{content:""}.sharkicon-icom-user-minus::before{content:""}.sharkicon-icom-user-check::before{content:""}.sharkicon-icom-user-tie::before{content:""}.sharkicon-icom-key::before{content:""}.sharkicon-icom-key2::before{content:""}.sharkicon-icom-happy::before{content:""}.sharkicon-icom-happy2::before{content:""}.sharkicon-icom-smile::before{content:""}.sharkicon-icom-smile2::before{content:""}.sharkicon-icom-tongue::before{content:""}.sharkicon-icom-tongue2::before{content:""}.sharkicon-icom-sad::before{content:""}.sharkicon-icom-sad2::before{content:""}.sharkicon-icom-wink::before{content:""}.sharkicon-icom-wink2::before{content:""}.sharkicon-icom-grin::before{content:""}.sharkicon-icom-grin2::before{content:""}.sharkicon-icom-cool::before{content:""}.sharkicon-icom-cool2::before{content:""}.sharkicon-icom-angry::before{content:""}.sharkicon-icom-angry2::before{content:""}.sharkicon-icom-evil::before{content:""}.sharkicon-icom-evil2::before{content:""}.sharkicon-icom-shocked::before{content:""}.sharkicon-icom-shocked2::before{content:""}.sharkicon-icom-baffled::before{content:""}.sharkicon-icom-baffled2::before{content:""}.sharkicon-icom-confused::before{content:""}.sharkicon-icom-confused2::before{content:""}.sharkicon-icom-neutral::before{content:""}.sharkicon-icom-neutral2::before{content:""}.sharkicon-icom-hipster::before{content:""}.sharkicon-icom-hipster2::before{content:""}.sharkicon-icom-wondering::before{content:""}.sharkicon-icom-wondering2::before{content:""}.sharkicon-icom-sleepy::before{content:""}.sharkicon-icom-sleepy2::before{content:""}.sharkicon-icom-frustrated::before{content:""}.sharkicon-icom-frustrated2::before{content:""}.sharkicon-icom-crying::before{content:""}.sharkicon-icom-crying2::before{content:""}.sharkicon-icom-spell-check::before{content:""}.sharkicon-icom-command-key::before{content:""}.sharkicon-icom-shift-key::before{content:""}.sharkicon-icom-control-key::before{content:""}.sharkicon-icom-option-key::before{content:""}.sharkicon-icom-wordpress::before{content:""}.sharkicon-icom-wordpress-square::before{content:""}.sharkicon-icom-yahoo::before{content:""}.sharkicon-icom-linux::before{content:""}.sharkicon-icom-finder::before{content:""}.sharkicon-icom-android::before{content:""}.sharkicon-icom-reddit::before{content:""}.sharkicon-icom-paypal::before{content:""}.sharkicon-icom-git::before{content:""}.sharkicon-octi-alignment-align::before{content:""}.sharkicon-octi-alignment-aligned-to::before{content:""}.sharkicon-octi-alignment-unalign::before{content:""}.sharkicon-octi-bookmark::before{content:""}.sharkicon-octi-broadcast::before{content:""}.sharkicon-octi-browser::before{content:""}.sharkicon-octi-checklist::before{content:""}.sharkicon-octi-circuit-board::before{content:""}.sharkicon-octi-clippy::before{content:""}.sharkicon-octi-cloud-download::before{content:""}.sharkicon-octi-cloud-upload::before{content:""}.sharkicon-octi-comment::before{content:""}.sharkicon-octi-comments::before{content:""}.sharkicon-octi-tach::before{content:""}.sharkicon-octi-device-camera::before{content:""}.sharkicon-octi-device-camera-video::before{content:""}.sharkicon-octi-device-desktop::before{content:""}.sharkicon-octi-diff::before{content:""}.sharkicon-octi-file-binary::before{content:""}.sharkicon-octi-file-media::before{content:""}.sharkicon-octi-file-submodule::before{content:""}.sharkicon-octi-file-symlink-directory::before{content:""}.sharkicon-octi-file-symlink-file::before{content:""}.sharkicon-octi-fold::before{content:""}.sharkicon-octi-git-branch::before{content:""}.sharkicon-octi-git-commit::before{content:""}.sharkicon-octi-git-compare::before{content:""}.sharkicon-octi-git-merge::before{content:""}.sharkicon-octi-git-pull-request::before{content:""}.sharkicon-octi-graph::before{content:""}.sharkicon-octi-home::before{content:""}.sharkicon-octi-horizontal-rule::before{content:""}.sharkicon-octi-key::before{content:""}.sharkicon-octi-light-bulb::before{content:""}.sharkicon-octi-link-external::before{content:""}.sharkicon-octi-lock::before{content:""}.sharkicon-octi-markdown::before{content:""}.sharkicon-octi-microscope::before{content:""}.sharkicon-octi-mirror::before{content:""}.sharkicon-octi-move-down::before{content:""}.sharkicon-octi-move-left::before{content:""}.sharkicon-octi-move-right::before{content:""}.sharkicon-octi-move-up::before{content:""}.sharkicon-octi-mute::before{content:""}.sharkicon-octi-organization::before{content:""}.sharkicon-octi-package::before{content:""}.sharkicon-octi-paintcan::before{content:""}.sharkicon-octi-person::before{content:""}.sharkicon-octi-plug::before{content:""}.sharkicon-octi-podium::before{content:""}.sharkicon-octi-pulse::before{content:""}.sharkicon-octi-puzzle::before{content:""}.sharkicon-octi-repo::before{content:""}.sharkicon-octi-repo-clone::before{content:""}.sharkicon-octi-repo-force-push::before{content:""}.sharkicon-octi-repo-forked::before{content:""}.sharkicon-octi-repo-pull::before{content:""}.sharkicon-octi-repo-push::before{content:""}.sharkicon-octi-rocket::before{content:""}.sharkicon-octi-ruby::before{content:""}.sharkicon-octi-screen-full::before{content:""}.sharkicon-octi-screen-normal::before{content:""}.sharkicon-octi-sign-in::before{content:""}.sharkicon-octi-sign-out::before{content:""}.sharkicon-octi-split::before{content:""}.sharkicon-octi-squirrel::before{content:""}.sharkicon-octi-steps::before{content:""}.sharkicon-octi-tag::before{content:""}.sharkicon-octi-telescope::before{content:""}.sharkicon-octi-terminal::before{content:""}.sharkicon-octi-unfold::before{content:""}.sharkicon-octi-versions::before{content:""}.sharkicon-glass::before{content:""}.sharkicon-music::before{content:""}.sharkicon-search::before{content:""}.sharkicon-envelope-o::before{content:""}.sharkicon-heart::before{content:""}.sharkicon-star::before{content:""}.sharkicon-star-o::before{content:""}.sharkicon-user::before{content:""}.sharkicon-film::before{content:""}.sharkicon-th-large::before{content:""}.sharkicon-th::before{content:""}.sharkicon-th-list::before{content:""}.sharkicon-check::before{content:""}.sharkicon-close::before{content:""}.sharkicon-search-plus::before{content:""}.sharkicon-search-minus::before{content:""}.sharkicon-power-off::before{content:""}.sharkicon-signal::before{content:""}.sharkicon-cog::before{content:""}.sharkicon-trash-o::before{content:""}.sharkicon-home::before{content:""}.sharkicon-file-o::before{content:""}.sharkicon-clock-o::before{content:""}.sharkicon-road::before{content:""}.sharkicon-download::before{content:""}.sharkicon-arrow-circle-o-down::before{content:""}.sharkicon-arrow-circle-o-up::before{content:""}.sharkicon-inbox::before{content:""}.sharkicon-play-circle-o::before{content:""}.sharkicon-repeat::before{content:""}.sharkicon-refresh::before{content:""}.sharkicon-list-alt::before{content:""}.sharkicon-lock::before{content:""}.sharkicon-flag::before{content:""}.sharkicon-headphones::before{content:""}.sharkicon-volume-off::before{content:""}.sharkicon-volume-down::before{content:""}.sharkicon-volume-up::before{content:""}.sharkicon-qrcode::before{content:""}.sharkicon-barcode::before{content:""}.sharkicon-tag::before{content:""}.sharkicon-tags::before{content:""}.sharkicon-book::before{content:""}.sharkicon-bookmark::before{content:""}.sharkicon-print::before{content:""}.sharkicon-camera::before{content:""}.sharkicon-font::before{content:""}.sharkicon-bold::before{content:""}.sharkicon-italic::before{content:""}.sharkicon-text-height::before{content:""}.sharkicon-text-width::before{content:""}.sharkicon-align-left::before{content:""}.sharkicon-align-center::before{content:""}.sharkicon-align-right::before{content:""}.sharkicon-align-justify::before{content:""}.sharkicon-list::before{content:""}.sharkicon-dedent::before{content:""}.sharkicon-indent::before{content:""}.sharkicon-video-camera::before{content:""}.sharkicon-image::before{content:""}.sharkicon-pencil::before{content:""}.sharkicon-map-marker::before{content:""}.sharkicon-adjust::before{content:""}.sharkicon-tint::before{content:""}.sharkicon-edit::before{content:""}.sharkicon-share-square-o::before{content:""}.sharkicon-check-square-o::before{content:""}.sharkicon-arrows::before{content:""}.sharkicon-step-backward::before{content:""}.sharkicon-fast-backward::before{content:""}.sharkicon-backward::before{content:""}.sharkicon-play::before{content:""}.sharkicon-pause::before{content:""}.sharkicon-stop::before{content:""}.sharkicon-forward::before{content:""}.sharkicon-fast-forward::before{content:""}.sharkicon-step-forward::before{content:""}.sharkicon-eject::before{content:""}.sharkicon-chevron-left::before{content:""}.sharkicon-chevron-right::before{content:""}.sharkicon-plus-circle::before{content:""}.sharkicon-minus-circle::before{content:""}.sharkicon-times-circle::before{content:""}.sharkicon-check-circle::before{content:""}.sharkicon-question-circle::before{content:""}.sharkicon-info-circle::before{content:""}.sharkicon-crosshairs::before{content:""}.sharkicon-times-circle-o::before{content:""}.sharkicon-check-circle-o::before{content:""}.sharkicon-ban::before{content:""}.sharkicon-arrow-left::before{content:""}.sharkicon-arrow-right::before{content:""}.sharkicon-arrow-up::before{content:""}.sharkicon-arrow-down::before{content:""}.sharkicon-mail-forward::before{content:""}.sharkicon-expand::before{content:""}.sharkicon-compress::before{content:""}.sharkicon-plus::before{content:""}.sharkicon-minus::before{content:""}.sharkicon-asterisk::before{content:""}.sharkicon-exclamation-circle::before{content:""}.sharkicon-gift::before{content:""}.sharkicon-leaf::before{content:""}.sharkicon-fire::before{content:""}.sharkicon-eye::before{content:""}.sharkicon-eye-slash::before{content:""}.sharkicon-exclamation-triangle::before{content:""}.sharkicon-plane::before{content:""}.sharkicon-calendar::before{content:""}.sharkicon-random::before{content:""}.sharkicon-comment::before{content:""}.sharkicon-magnet::before{content:""}.sharkicon-chevron-up::before{content:""}.sharkicon-chevron-down::before{content:""}.sharkicon-retweet::before{content:""}.sharkicon-shopping-cart::before{content:""}.sharkicon-folder::before{content:""}.sharkicon-folder-open::before{content:""}.sharkicon-arrows-v::before{content:""}.sharkicon-arrows-h::before{content:""}.sharkicon-bar-chart::before{content:""}.sharkicon-twitter-square::before{content:""}.sharkicon-facebook-square::before{content:""}.sharkicon-camera-retro::before{content:""}.sharkicon-key::before{content:""}.sharkicon-cogs::before{content:""}.sharkicon-comments::before{content:""}.sharkicon-thumbs-o-up::before{content:""}.sharkicon-thumbs-o-down::before{content:""}.sharkicon-star-half::before{content:""}.sharkicon-heart-o::before{content:""}.sharkicon-sign-out::before{content:""}.sharkicon-linkedin-square::before{content:""}.sharkicon-thumb-tack::before{content:""}.sharkicon-external-link::before{content:""}.sharkicon-sign-in::before{content:""}.sharkicon-trophy::before{content:""}.sharkicon-github-square::before{content:""}.sharkicon-upload::before{content:""}.sharkicon-lemon-o::before{content:""}.sharkicon-phone::before{content:""}.sharkicon-square-o::before{content:""}.sharkicon-bookmark-o::before{content:""}.sharkicon-phone-square::before{content:""}.sharkicon-twitter::before{content:""}.sharkicon-facebook::before{content:""}.sharkicon-github::before{content:""}.sharkicon-unlock::before{content:""}.sharkicon-credit-card::before{content:""}.sharkicon-feed::before{content:""}.sharkicon-hdd-o::before{content:""}.sharkicon-bullhorn::before{content:""}.sharkicon-bell-o::before{content:""}.sharkicon-certificate::before{content:""}.sharkicon-hand-o-right::before{content:""}.sharkicon-hand-o-left::before{content:""}.sharkicon-hand-o-up::before{content:""}.sharkicon-hand-o-down::before{content:""}.sharkicon-arrow-circle-left::before{content:""}.sharkicon-arrow-circle-right::before{content:""}.sharkicon-arrow-circle-up::before{content:""}.sharkicon-arrow-circle-down::before{content:""}.sharkicon-globe::before{content:""}.sharkicon-wrench::before{content:""}.sharkicon-tasks::before{content:""}.sharkicon-filter::before{content:""}.sharkicon-briefcase::before{content:""}.sharkicon-arrows-alt::before{content:""}.sharkicon-group::before{content:""}.sharkicon-chain::before{content:""}.sharkicon-cloud::before{content:""}.sharkicon-flask::before{content:""}.sharkicon-cut::before{content:""}.sharkicon-copy::before{content:""}.sharkicon-paperclip::before{content:""}.sharkicon-floppy-o::before{content:""}.sharkicon-square::before{content:""}.sharkicon-bars::before{content:""}.sharkicon-list-ul::before{content:""}.sharkicon-list-ol::before{content:""}.sharkicon-strikethrough::before{content:""}.sharkicon-underline::before{content:""}.sharkicon-table::before{content:""}.sharkicon-magic::before{content:""}.sharkicon-truck::before{content:""}.sharkicon-pinterest::before{content:""}.sharkicon-pinterest-square::before{content:""}.sharkicon-google-plus-square::before{content:""}.sharkicon-google-plus::before{content:""}.sharkicon-money::before{content:""}.sharkicon-caret-down::before{content:""}.sharkicon-caret-up::before{content:""}.sharkicon-caret-left::before{content:""}.sharkicon-caret-right::before{content:""}.sharkicon-columns::before{content:""}.sharkicon-sort::before{content:""}.sharkicon-sort-desc::before{content:""}.sharkicon-sort-asc::before{content:""}.sharkicon-envelope::before{content:""}.sharkicon-linkedin::before{content:""}.sharkicon-rotate-left::before{content:""}.sharkicon-gavel::before{content:""}.sharkicon-dashboard::before{content:""}.sharkicon-comment-o::before{content:""}.sharkicon-comments-o::before{content:""}.sharkicon-bolt::before{content:""}.sharkicon-sitemap::before{content:""}.sharkicon-umbrella::before{content:""}.sharkicon-clipboard::before{content:""}.sharkicon-lightbulb-o::before{content:""}.sharkicon-exchange::before{content:""}.sharkicon-cloud-download::before{content:""}.sharkicon-cloud-upload::before{content:""}.sharkicon-user-md::before{content:""}.sharkicon-stethoscope::before{content:""}.sharkicon-suitcase::before{content:""}.sharkicon-bell::before{content:""}.sharkicon-coffee::before{content:""}.sharkicon-cutlery::before{content:""}.sharkicon-file-text-o::before{content:""}.sharkicon-building-o::before{content:""}.sharkicon-hospital-o::before{content:""}.sharkicon-ambulance::before{content:""}.sharkicon-medkit::before{content:""}.sharkicon-fighter-jet::before{content:""}.sharkicon-beer::before{content:""}.sharkicon-h-square::before{content:""}.sharkicon-plus-square::before{content:""}.sharkicon-angle-double-left::before{content:""}.sharkicon-angle-double-right::before{content:""}.sharkicon-angle-double-up::before{content:""}.sharkicon-angle-double-down::before{content:""}.sharkicon-angle-left::before{content:""}.sharkicon-angle-right::before{content:""}.sharkicon-angle-up::before{content:""}.sharkicon-angle-down::before{content:""}.sharkicon-desktop::before{content:""}.sharkicon-laptop::before{content:""}.sharkicon-tablet::before{content:""}.sharkicon-mobile::before{content:""}.sharkicon-circle-o::before{content:""}.sharkicon-quote-left::before{content:""}.sharkicon-quote-right::before{content:""}.sharkicon-spinner::before{content:""}.sharkicon-circle::before{content:""}.sharkicon-mail-reply::before{content:""}.sharkicon-github-alt::before{content:""}.sharkicon-folder-o::before{content:""}.sharkicon-folder-open-o::before{content:""}.sharkicon-smile-o::before{content:""}.sharkicon-frown-o::before{content:""}.sharkicon-meh-o::before{content:""}.sharkicon-gamepad::before{content:""}.sharkicon-keyboard-o::before{content:""}.sharkicon-flag-o::before{content:""}.sharkicon-flag-checkered::before{content:""}.sharkicon-terminal::before{content:""}.sharkicon-code::before{content:""}.sharkicon-mail-reply-all::before{content:""}.sharkicon-star-half-empty::before{content:""}.sharkicon-location-arrow::before{content:""}.sharkicon-crop::before{content:""}.sharkicon-code-fork::before{content:""}.sharkicon-chain-broken::before{content:""}.sharkicon-question::before{content:""}.sharkicon-info::before{content:""}.sharkicon-exclamation::before{content:""}.sharkicon-superscript::before{content:""}.sharkicon-subscript::before{content:""}.sharkicon-eraser::before{content:""}.sharkicon-puzzle-piece::before{content:""}.sharkicon-microphone::before{content:""}.sharkicon-microphone-slash::before{content:""}.sharkicon-shield::before{content:""}.sharkicon-calendar-o::before{content:""}.sharkicon-fire-extinguisher::before{content:""}.sharkicon-rocket::before{content:""}.sharkicon-maxcdn::before{content:""}.sharkicon-chevron-circle-left::before{content:""}.sharkicon-chevron-circle-right::before{content:""}.sharkicon-chevron-circle-up::before{content:""}.sharkicon-chevron-circle-down::before{content:""}.sharkicon-html5::before{content:""}.sharkicon-css3::before{content:""}.sharkicon-anchor::before{content:""}.sharkicon-unlock-alt::before{content:""}.sharkicon-bullseye::before{content:""}.sharkicon-ellipsis-h::before{content:""}.sharkicon-ellipsis-v::before{content:""}.sharkicon-rss-square::before{content:""}.sharkicon-play-circle::before{content:""}.sharkicon-ticket::before{content:""}.sharkicon-minus-square::before{content:""}.sharkicon-minus-square-o::before{content:""}.sharkicon-level-up::before{content:""}.sharkicon-level-down::before{content:""}.sharkicon-check-square::before{content:""}.sharkicon-pencil-square::before{content:""}.sharkicon-external-link-square::before{content:""}.sharkicon-share-square::before{content:""}.sharkicon-compass::before{content:""}.sharkicon-caret-square-o-down::before{content:""}.sharkicon-caret-square-o-up::before{content:""}.sharkicon-caret-square-o-right::before{content:""}.sharkicon-eur::before{content:""}.sharkicon-gbp::before{content:""}.sharkicon-dollar::before{content:""}.sharkicon-inr::before{content:""}.sharkicon-cny::before{content:""}.sharkicon-rouble::before{content:""}.sharkicon-krw::before{content:""}.sharkicon-bitcoin::before{content:""}.sharkicon-file::before{content:""}.sharkicon-file-text::before{content:""}.sharkicon-sort-alpha-asc::before{content:""}.sharkicon-sort-alpha-desc::before{content:""}.sharkicon-sort-amount-asc::before{content:""}.sharkicon-sort-amount-desc::before{content:""}.sharkicon-sort-numeric-asc::before{content:""}.sharkicon-sort-numeric-desc::before{content:""}.sharkicon-thumbs-up::before{content:""}.sharkicon-thumbs-down::before{content:""}.sharkicon-youtube-square::before{content:""}.sharkicon-youtube::before{content:""}.sharkicon-xing::before{content:""}.sharkicon-xing-square::before{content:""}.sharkicon-youtube-play::before{content:""}.sharkicon-dropbox::before{content:""}.sharkicon-stack-overflow::before{content:""}.sharkicon-instagram::before{content:""}.sharkicon-flickr::before{content:""}.sharkicon-adn::before{content:""}.sharkicon-bitbucket::before{content:""}.sharkicon-bitbucket-square::before{content:""}.sharkicon-tumblr::before{content:""}.sharkicon-tumblr-square::before{content:""}.sharkicon-long-arrow-down::before{content:""}.sharkicon-long-arrow-up::before{content:""}.sharkicon-long-arrow-left::before{content:""}.sharkicon-long-arrow-right::before{content:""}.sharkicon-apple::before{content:""}.sharkicon-windows::before{content:""}.sharkicon-android::before{content:""}.sharkicon-linux::before{content:""}.sharkicon-dribbble::before{content:""}.sharkicon-skype::before{content:""}.sharkicon-foursquare::before{content:""}.sharkicon-trello::before{content:""}.sharkicon-female::before{content:""}.sharkicon-male::before{content:""}.sharkicon-gittip::before{content:""}.sharkicon-sun-o::before{content:""}.sharkicon-moon-o::before{content:""}.sharkicon-archive::before{content:""}.sharkicon-bug::before{content:""}.sharkicon-vk::before{content:""}.sharkicon-weibo::before{content:""}.sharkicon-renren::before{content:""}.sharkicon-pagelines::before{content:""}.sharkicon-stack-exchange::before{content:""}.sharkicon-arrow-circle-o-right::before{content:""}.sharkicon-arrow-circle-o-left::before{content:""}.sharkicon-caret-square-o-left::before{content:""}.sharkicon-dot-circle-o::before{content:""}.sharkicon-wheelchair::before{content:""}.sharkicon-vimeo-square::before{content:""}.sharkicon-try::before{content:""}.sharkicon-plus-square-o::before{content:""}.sharkicon-space-shuttle::before{content:""}.sharkicon-slack::before{content:""}.sharkicon-envelope-square::before{content:""}.sharkicon-wordpress::before{content:""}.sharkicon-openid::before{content:""}.sharkicon-bank::before{content:""}.sharkicon-graduation-cap::before{content:""}.sharkicon-yahoo::before{content:""}.sharkicon-google::before{content:""}.sharkicon-reddit::before{content:""}.sharkicon-reddit-square::before{content:""}.sharkicon-stumbleupon-circle::before{content:""}.sharkicon-stumbleupon::before{content:""}.sharkicon-delicious::before{content:""}.sharkicon-digg::before{content:""}.sharkicon-pied-piper::before{content:""}.sharkicon-pied-piper-alt::before{content:""}.sharkicon-drupal::before{content:""}.sharkicon-joomla::before{content:""}.sharkicon-language::before{content:""}.sharkicon-fax::before{content:""}.sharkicon-building::before{content:""}.sharkicon-child::before{content:""}.sharkicon-paw::before{content:""}.sharkicon-spoon::before{content:""}.sharkicon-cube::before{content:""}.sharkicon-cubes::before{content:""}.sharkicon-behance::before{content:""}.sharkicon-behance-square::before{content:""}.sharkicon-steam::before{content:""}.sharkicon-steam-square::before{content:""}.sharkicon-recycle::before{content:""}.sharkicon-automobile::before{content:""}.sharkicon-cab::before{content:""}.sharkicon-tree::before{content:""}.sharkicon-spotify::before{content:""}.sharkicon-deviantart::before{content:""}.sharkicon-soundcloud::before{content:""}.sharkicon-database::before{content:""}.sharkicon-file-pdf-o::before{content:""}.sharkicon-file-word-o::before{content:""}.sharkicon-file-excel-o::before{content:""}.sharkicon-file-powerpoint-o::before{content:""}.sharkicon-file-image-o::before{content:""}.sharkicon-file-archive-o::before{content:""}.sharkicon-file-audio-o::before{content:""}.sharkicon-file-movie-o::before{content:""}.sharkicon-file-code-o::before{content:""}.sharkicon-vine::before{content:""}.sharkicon-codepen::before{content:""}.sharkicon-jsfiddle::before{content:""}.sharkicon-life-bouy::before{content:""}.sharkicon-circle-o-notch::before{content:""}.sharkicon-ra::before{content:""}.sharkicon-empire::before{content:""}.sharkicon-git-square::before{content:""}.sharkicon-git::before{content:""}.sharkicon-hacker-news::before{content:""}.sharkicon-tencent-weibo::before{content:""}.sharkicon-qq::before{content:""}.sharkicon-wechat::before{content:""}.sharkicon-paper-plane::before{content:""}.sharkicon-paper-plane-o::before{content:""}.sharkicon-history::before{content:""}.sharkicon-circle-thin::before{content:""}.sharkicon-header::before{content:""}.sharkicon-paragraph::before{content:""}.sharkicon-sliders::before{content:""}.sharkicon-share-alt::before{content:""}.sharkicon-share-alt-square::before{content:""}.sharkicon-bomb::before{content:""}.sharkicon-futbol-o::before{content:""}.sharkicon-tty::before{content:""}.sharkicon-binoculars::before{content:""}.sharkicon-plug::before{content:""}.sharkicon-slideshare::before{content:""}.sharkicon-twitch::before{content:""}.sharkicon-yelp::before{content:""}.sharkicon-newspaper-o::before{content:""}.sharkicon-wifi::before{content:""}.sharkicon-calculator::before{content:""}.sharkicon-paypal::before{content:""}.sharkicon-google-wallet::before{content:""}.sharkicon-cc-visa::before{content:""}.sharkicon-cc-mastercard::before{content:""}.sharkicon-cc-discover::before{content:""}.sharkicon-cc-amex::before{content:""}.sharkicon-cc-paypal::before{content:""}.sharkicon-cc-stripe::before{content:""}.sharkicon-bell-slash::before{content:""}.sharkicon-bell-slash-o::before{content:""}.sharkicon-trash::before{content:""}.sharkicon-copyright::before{content:""}.sharkicon-at::before{content:""}.sharkicon-eyedropper::before{content:""}.sharkicon-paint-brush::before{content:""}.sharkicon-birthday-cake::before{content:""}.sharkicon-area-chart::before{content:""}.sharkicon-pie-chart::before{content:""}.sharkicon-line-chart::before{content:""}.sharkicon-lastfm::before{content:""}.sharkicon-lastfm-square::before{content:""}.sharkicon-toggle-off::before{content:""}.sharkicon-toggle-on::before{content:""}.sharkicon-bicycle::before{content:""}.sharkicon-bus::before{content:""}.sharkicon-ioxhost::before{content:""}.sharkicon-angellist::before{content:""}.sharkicon-cc::before{content:""}.sharkicon-ils::before{content:""}.sharkicon-meanpath::before{content:""}.sharkicon-buysellads::before{content:""}.sharkicon-connectdevelop::before{content:""}.sharkicon-dashcube::before{content:""}.sharkicon-forumbee::before{content:""}.sharkicon-leanpub::before{content:""}.sharkicon-sellsy::before{content:""}.sharkicon-shirtsinbulk::before{content:""}.sharkicon-simplybuilt::before{content:""}.sharkicon-skyatlas::before{content:""}.sharkicon-cart-plus::before{content:""}.sharkicon-cart-arrow-down::before{content:""}.sharkicon-diamond::before{content:""}.sharkicon-ship::before{content:""}.sharkicon-user-secret::before{content:""}.sharkicon-motorcycle::before{content:""}.sharkicon-street-view::before{content:""}.sharkicon-heartbeat::before{content:""}.sharkicon-venus::before{content:""}.sharkicon-mars::before{content:""}.sharkicon-mercury::before{content:""}.sharkicon-intersex::before{content:""}.sharkicon-transgender-alt::before{content:""}.sharkicon-venus-double::before{content:""}.sharkicon-mars-double::before{content:""}.sharkicon-venus-mars::before{content:""}.sharkicon-mars-stroke::before{content:""}.sharkicon-mars-stroke-v::before{content:""}.sharkicon-mars-stroke-h::before{content:""}.sharkicon-neuter::before{content:""}.sharkicon-genderless::before{content:""}.sharkicon-facebook-official::before{content:""}.sharkicon-pinterest-p::before{content:""}.sharkicon-whatsapp::before{content:""}.sharkicon-server::before{content:""}.sharkicon-user-plus::before{content:""}.sharkicon-user-times::before{content:""}.sharkicon-bed::before{content:""}.sharkicon-viacoin::before{content:""}.sharkicon-train::before{content:""}.sharkicon-subway::before{content:""}.sharkicon-medium::before{content:""}.sharkicon-y-combinator::before{content:""}.sharkicon-optin-monster::before{content:""}.sharkicon-opencart::before{content:""}.sharkicon-expeditedssl::before{content:""}.sharkicon-battery-4::before{content:""}.sharkicon-battery-3::before{content:""}.sharkicon-battery-2::before{content:""}.sharkicon-battery-1::before{content:""}.sharkicon-battery-0::before{content:""}.sharkicon-mouse-pointer::before{content:""}.sharkicon-i-cursor::before{content:""}.sharkicon-object-group::before{content:""}.sharkicon-object-ungroup::before{content:""}.sharkicon-sticky-note::before{content:""}.sharkicon-sticky-note-o::before{content:""}.sharkicon-cc-jcb::before{content:""}.sharkicon-cc-diners-club::before{content:""}.sharkicon-clone::before{content:""}.sharkicon-balance-scale::before{content:""}.sharkicon-hourglass-o::before{content:""}.sharkicon-hourglass-1::before{content:""}.sharkicon-hourglass-2::before{content:""}.sharkicon-hourglass-3::before{content:""}.sharkicon-hourglass::before{content:""}.sharkicon-hand-grab-o::before{content:""}.sharkicon-hand-paper-o::before{content:""}.sharkicon-hand-scissors-o::before{content:""}.sharkicon-hand-lizard-o::before{content:""}.sharkicon-hand-spock-o::before{content:""}.sharkicon-hand-pointer-o::before{content:""}.sharkicon-hand-peace-o::before{content:""}.sharkicon-trademark::before{content:""}.sharkicon-registered::before{content:""}.sharkicon-creative-commons::before{content:""}.sharkicon-gg::before{content:""}.sharkicon-gg-circle::before{content:""}.sharkicon-tripadvisor::before{content:""}.sharkicon-odnoklassniki::before{content:""}.sharkicon-odnoklassniki-square::before{content:""}.sharkicon-get-pocket::before{content:""}.sharkicon-wikipedia-w::before{content:""}.sharkicon-safari::before{content:""}.sharkicon-chrome::before{content:""}.sharkicon-firefox::before{content:""}.sharkicon-opera::before{content:""}.sharkicon-internet-explorer::before{content:""}.sharkicon-television::before{content:""}.sharkicon-contao::before{content:""}.sharkicon-500px::before{content:""}.sharkicon-amazon::before{content:""}.sharkicon-calendar-plus-o::before{content:""}.sharkicon-calendar-minus-o::before{content:""}.sharkicon-calendar-times-o::before{content:""}.sharkicon-calendar-check-o::before{content:""}.sharkicon-industry::before{content:""}.sharkicon-map-pin::before{content:""}.sharkicon-map-signs::before{content:""}.sharkicon-map-o::before{content:""}.sharkicon-map::before{content:""}.sharkicon-commenting::before{content:""}.sharkicon-commenting-o::before{content:""}.sharkicon-houzz::before{content:""}.sharkicon-vimeo::before{content:""}.sharkicon-black-tie::before{content:""}.sharkicon-fonticons::before{content:""}.sharkicon-lg{font-size:1.33333333em;line-height:0.75em;vertical-align:-15%}.sharkicon-2x{font-size:2em}.sharkicon-3x{font-size:3em}.sharkicon-4x{font-size:4em}.sharkicon-5x{font-size:5em}.sharkicon-fw{text-align:center;width:1.28571429em}.sharkicon-border{border-radius:.1em;padding:.2em .25em .15em;border:solid 0.08em}.sharkicon-pull-left{float:left;margin-right:.3em}.sharkicon-pull-right{float:right;margin-left:.3em}.sharkicon-rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.sharkicon-rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.sharkicon-rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.sharkicon-flip-horizontal{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.sharkicon-flip-vertical{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.sharkicon-inverse{-webkit-filter:invert(100%);filter:invert(100%)}.sharkicon-spin{-webkit-animation:sharkicon-animation-spin 2s infinite linear;-moz-animation:sharkicon-animation-spin 2s infinite linear;animation:sharkicon-animation-spin 2s infinite linear}.sharkicon-pulse{-webkit-animation:sharkicon-animation-spin 1s infinite steps(8);-moz-animation:sharkicon-animation-spin 1s infinite steps(8);animation:sharkicon-animation-spin 1s infinite steps(8)}.sharkicon-ul{padding-left:0;list-style-type:none;margin-left:2.14285714em}.sharkicon-ul>li{position:relative}.sharkicon-ul>li .sharkicon-li{text-align:center;position:absolute;left:-2.14285714em;width:2.14285714em;top:0.14285714em}.sharkicon-ul>li .sharkicon-li .sharkicon-lg{left:-1.85714286em}.sharkicon-stack{width:2em;height:2em;line-height:2em;vertical-align:middle;position:relative;display:inline-block}.sharkicon-stack .sharkicon-stack-1x,.sharkicon-stack .sharkicon-stack-2x{left:0;width:100%;text-align:center;position:absolute}.sharkicon-stack .sharkicon-stack-1x{line-height:inherit}.sharkicon-stack .sharkicon-stack-2x{font-size:2em}@-webkit-keyframes sharkicon-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-moz-keyframes sharkicon-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@keyframes sharkicon-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}} -/*# sourceMappingURL=long-classes.min.css.map */ \ No newline at end of file diff --git a/src/vendor/websharks/sharkicons/src/short-classes.min.css b/src/vendor/websharks/sharkicons/src/short-classes.min.css index 8603fcf..64322cd 100644 --- a/src/vendor/websharks/sharkicons/src/short-classes.min.css +++ b/src/vendor/websharks/sharkicons/src/short-classes.min.css @@ -1,3 +1,2 @@ @font-face{font-family:sharkicons;src:url("fonts/sharkicons.eot?v160221");src:url("fonts/sharkicons.eot?#iefix&v160221") format("embedded-opentype"),url("fonts/sharkicons.ttf?v160221") format("truetype"),url("fonts/sharkicons.woff?v160221") format("woff"),url("fonts/sharkicons.svg?v160221#sharkicons") format("svg");font-weight:normal;font-style:normal}.si::before{font:normal normal normal 14px/1 sharkicons;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;display:inline-block;font-size:inherit;text-decoration:inherit;text-transform:none}.si-broom::before{content:""}.si-comment-mail-one::before{content:""}.si-comment-mail::before{content:""}.si-s2member::before{content:""}.si-websharks::before{content:""}.si-wp-kb-articles::before{content:""}.si-zencache-logo::before{content:""}.si-zencache::before{content:""}.si-wp-sharks::before{content:""}.si-wp-sharks-fin::before{content:""}.si-comet-cache::before{content:""}.si-comet-cache-logo::before{content:""}.si-comet-cache-comet::before{content:""}.si-feat-watch::before{content:""}.si-feat-server::before{content:""}.si-feat-layers::before{content:""}.si-feat-box::before{content:""}.si-feat-ellipsis::before{content:""}.si-typi-group::before{content:""}.si-enty-bookmark::before{content:""}.si-enty-bookmarks::before{content:""}.si-enty-open-book::before{content:""}.si-enty-archive::before{content:""}.si-enty-area-graph::before{content:""}.si-enty-bucket::before{content:""}.si-enty-colors::before{content:""}.si-enty-copy::before{content:""}.si-enty-drive::before{content:""}.si-enty-feather::before{content:""}.si-enty-gauge::before{content:""}.si-enty-hand::before{content:""}.si-enty-lab-flask::before{content:""}.si-enty-mask::before{content:""}.si-enty-medal::before{content:""}.si-enty-exclamation::before{content:""}.si-enty-palette::before{content:""}.si-enty-ruler::before{content:""}.si-enty-shop::before{content:""}.si-enty-basket::before{content:""}.si-enty-cart::before{content:""}.si-enty-traffic-cone::before{content:""}.si-enty-tree::before{content:""}.si-enty-trophy::before{content:""}.si-enty-v-card::before{content:""}.si-enty-google-hangouts::before{content:""}.si-eleg-line-graph::before{content:""}.si-eleg-male::before{content:""}.si-eleg-female::before{content:""}.si-eleg-atom::before{content:""}.si-broc-cart::before{content:""}.si-broc-crap::before{content:""}.si-broc-atom::before{content:""}.si-icom-headphones::before{content:""}.si-icom-barcode::before{content:""}.si-icom-user::before{content:""}.si-icom-users::before{content:""}.si-icom-user-plus::before{content:""}.si-icom-user-minus::before{content:""}.si-icom-user-check::before{content:""}.si-icom-user-tie::before{content:""}.si-icom-key::before{content:""}.si-icom-key2::before{content:""}.si-icom-happy::before{content:""}.si-icom-happy2::before{content:""}.si-icom-smile::before{content:""}.si-icom-smile2::before{content:""}.si-icom-tongue::before{content:""}.si-icom-tongue2::before{content:""}.si-icom-sad::before{content:""}.si-icom-sad2::before{content:""}.si-icom-wink::before{content:""}.si-icom-wink2::before{content:""}.si-icom-grin::before{content:""}.si-icom-grin2::before{content:""}.si-icom-cool::before{content:""}.si-icom-cool2::before{content:""}.si-icom-angry::before{content:""}.si-icom-angry2::before{content:""}.si-icom-evil::before{content:""}.si-icom-evil2::before{content:""}.si-icom-shocked::before{content:""}.si-icom-shocked2::before{content:""}.si-icom-baffled::before{content:""}.si-icom-baffled2::before{content:""}.si-icom-confused::before{content:""}.si-icom-confused2::before{content:""}.si-icom-neutral::before{content:""}.si-icom-neutral2::before{content:""}.si-icom-hipster::before{content:""}.si-icom-hipster2::before{content:""}.si-icom-wondering::before{content:""}.si-icom-wondering2::before{content:""}.si-icom-sleepy::before{content:""}.si-icom-sleepy2::before{content:""}.si-icom-frustrated::before{content:""}.si-icom-frustrated2::before{content:""}.si-icom-crying::before{content:""}.si-icom-crying2::before{content:""}.si-icom-spell-check::before{content:""}.si-icom-command-key::before{content:""}.si-icom-shift-key::before{content:""}.si-icom-control-key::before{content:""}.si-icom-option-key::before{content:""}.si-icom-wordpress::before{content:""}.si-icom-wordpress-square::before{content:""}.si-icom-yahoo::before{content:""}.si-icom-linux::before{content:""}.si-icom-finder::before{content:""}.si-icom-android::before{content:""}.si-icom-reddit::before{content:""}.si-icom-paypal::before{content:""}.si-icom-git::before{content:""}.si-octi-alignment-align::before{content:""}.si-octi-alignment-aligned-to::before{content:""}.si-octi-alignment-unalign::before{content:""}.si-octi-bookmark::before{content:""}.si-octi-broadcast::before{content:""}.si-octi-browser::before{content:""}.si-octi-checklist::before{content:""}.si-octi-circuit-board::before{content:""}.si-octi-clippy::before{content:""}.si-octi-cloud-download::before{content:""}.si-octi-cloud-upload::before{content:""}.si-octi-comment::before{content:""}.si-octi-comments::before{content:""}.si-octi-tach::before{content:""}.si-octi-device-camera::before{content:""}.si-octi-device-camera-video::before{content:""}.si-octi-device-desktop::before{content:""}.si-octi-diff::before{content:""}.si-octi-file-binary::before{content:""}.si-octi-file-media::before{content:""}.si-octi-file-submodule::before{content:""}.si-octi-file-symlink-directory::before{content:""}.si-octi-file-symlink-file::before{content:""}.si-octi-fold::before{content:""}.si-octi-git-branch::before{content:""}.si-octi-git-commit::before{content:""}.si-octi-git-compare::before{content:""}.si-octi-git-merge::before{content:""}.si-octi-git-pull-request::before{content:""}.si-octi-graph::before{content:""}.si-octi-home::before{content:""}.si-octi-horizontal-rule::before{content:""}.si-octi-key::before{content:""}.si-octi-light-bulb::before{content:""}.si-octi-link-external::before{content:""}.si-octi-lock::before{content:""}.si-octi-markdown::before{content:""}.si-octi-microscope::before{content:""}.si-octi-mirror::before{content:""}.si-octi-move-down::before{content:""}.si-octi-move-left::before{content:""}.si-octi-move-right::before{content:""}.si-octi-move-up::before{content:""}.si-octi-mute::before{content:""}.si-octi-organization::before{content:""}.si-octi-package::before{content:""}.si-octi-paintcan::before{content:""}.si-octi-person::before{content:""}.si-octi-plug::before{content:""}.si-octi-podium::before{content:""}.si-octi-pulse::before{content:""}.si-octi-puzzle::before{content:""}.si-octi-repo::before{content:""}.si-octi-repo-clone::before{content:""}.si-octi-repo-force-push::before{content:""}.si-octi-repo-forked::before{content:""}.si-octi-repo-pull::before{content:""}.si-octi-repo-push::before{content:""}.si-octi-rocket::before{content:""}.si-octi-ruby::before{content:""}.si-octi-screen-full::before{content:""}.si-octi-screen-normal::before{content:""}.si-octi-sign-in::before{content:""}.si-octi-sign-out::before{content:""}.si-octi-split::before{content:""}.si-octi-squirrel::before{content:""}.si-octi-steps::before{content:""}.si-octi-tag::before{content:""}.si-octi-telescope::before{content:""}.si-octi-terminal::before{content:""}.si-octi-unfold::before{content:""}.si-octi-versions::before{content:""}.si-glass::before{content:""}.si-music::before{content:""}.si-search::before{content:""}.si-envelope-o::before{content:""}.si-heart::before{content:""}.si-star::before{content:""}.si-star-o::before{content:""}.si-user::before{content:""}.si-film::before{content:""}.si-th-large::before{content:""}.si-th::before{content:""}.si-th-list::before{content:""}.si-check::before{content:""}.si-close::before{content:""}.si-search-plus::before{content:""}.si-search-minus::before{content:""}.si-power-off::before{content:""}.si-signal::before{content:""}.si-cog::before{content:""}.si-trash-o::before{content:""}.si-home::before{content:""}.si-file-o::before{content:""}.si-clock-o::before{content:""}.si-road::before{content:""}.si-download::before{content:""}.si-arrow-circle-o-down::before{content:""}.si-arrow-circle-o-up::before{content:""}.si-inbox::before{content:""}.si-play-circle-o::before{content:""}.si-repeat::before{content:""}.si-refresh::before{content:""}.si-list-alt::before{content:""}.si-lock::before{content:""}.si-flag::before{content:""}.si-headphones::before{content:""}.si-volume-off::before{content:""}.si-volume-down::before{content:""}.si-volume-up::before{content:""}.si-qrcode::before{content:""}.si-barcode::before{content:""}.si-tag::before{content:""}.si-tags::before{content:""}.si-book::before{content:""}.si-bookmark::before{content:""}.si-print::before{content:""}.si-camera::before{content:""}.si-font::before{content:""}.si-bold::before{content:""}.si-italic::before{content:""}.si-text-height::before{content:""}.si-text-width::before{content:""}.si-align-left::before{content:""}.si-align-center::before{content:""}.si-align-right::before{content:""}.si-align-justify::before{content:""}.si-list::before{content:""}.si-dedent::before{content:""}.si-indent::before{content:""}.si-video-camera::before{content:""}.si-image::before{content:""}.si-pencil::before{content:""}.si-map-marker::before{content:""}.si-adjust::before{content:""}.si-tint::before{content:""}.si-edit::before{content:""}.si-share-square-o::before{content:""}.si-check-square-o::before{content:""}.si-arrows::before{content:""}.si-step-backward::before{content:""}.si-fast-backward::before{content:""}.si-backward::before{content:""}.si-play::before{content:""}.si-pause::before{content:""}.si-stop::before{content:""}.si-forward::before{content:""}.si-fast-forward::before{content:""}.si-step-forward::before{content:""}.si-eject::before{content:""}.si-chevron-left::before{content:""}.si-chevron-right::before{content:""}.si-plus-circle::before{content:""}.si-minus-circle::before{content:""}.si-times-circle::before{content:""}.si-check-circle::before{content:""}.si-question-circle::before{content:""}.si-info-circle::before{content:""}.si-crosshairs::before{content:""}.si-times-circle-o::before{content:""}.si-check-circle-o::before{content:""}.si-ban::before{content:""}.si-arrow-left::before{content:""}.si-arrow-right::before{content:""}.si-arrow-up::before{content:""}.si-arrow-down::before{content:""}.si-mail-forward::before{content:""}.si-expand::before{content:""}.si-compress::before{content:""}.si-plus::before{content:""}.si-minus::before{content:""}.si-asterisk::before{content:""}.si-exclamation-circle::before{content:""}.si-gift::before{content:""}.si-leaf::before{content:""}.si-fire::before{content:""}.si-eye::before{content:""}.si-eye-slash::before{content:""}.si-exclamation-triangle::before{content:""}.si-plane::before{content:""}.si-calendar::before{content:""}.si-random::before{content:""}.si-comment::before{content:""}.si-magnet::before{content:""}.si-chevron-up::before{content:""}.si-chevron-down::before{content:""}.si-retweet::before{content:""}.si-shopping-cart::before{content:""}.si-folder::before{content:""}.si-folder-open::before{content:""}.si-arrows-v::before{content:""}.si-arrows-h::before{content:""}.si-bar-chart::before{content:""}.si-twitter-square::before{content:""}.si-facebook-square::before{content:""}.si-camera-retro::before{content:""}.si-key::before{content:""}.si-cogs::before{content:""}.si-comments::before{content:""}.si-thumbs-o-up::before{content:""}.si-thumbs-o-down::before{content:""}.si-star-half::before{content:""}.si-heart-o::before{content:""}.si-sign-out::before{content:""}.si-linkedin-square::before{content:""}.si-thumb-tack::before{content:""}.si-external-link::before{content:""}.si-sign-in::before{content:""}.si-trophy::before{content:""}.si-github-square::before{content:""}.si-upload::before{content:""}.si-lemon-o::before{content:""}.si-phone::before{content:""}.si-square-o::before{content:""}.si-bookmark-o::before{content:""}.si-phone-square::before{content:""}.si-twitter::before{content:""}.si-facebook::before{content:""}.si-github::before{content:""}.si-unlock::before{content:""}.si-credit-card::before{content:""}.si-feed::before{content:""}.si-hdd-o::before{content:""}.si-bullhorn::before{content:""}.si-bell-o::before{content:""}.si-certificate::before{content:""}.si-hand-o-right::before{content:""}.si-hand-o-left::before{content:""}.si-hand-o-up::before{content:""}.si-hand-o-down::before{content:""}.si-arrow-circle-left::before{content:""}.si-arrow-circle-right::before{content:""}.si-arrow-circle-up::before{content:""}.si-arrow-circle-down::before{content:""}.si-globe::before{content:""}.si-wrench::before{content:""}.si-tasks::before{content:""}.si-filter::before{content:""}.si-briefcase::before{content:""}.si-arrows-alt::before{content:""}.si-group::before{content:""}.si-chain::before{content:""}.si-cloud::before{content:""}.si-flask::before{content:""}.si-cut::before{content:""}.si-copy::before{content:""}.si-paperclip::before{content:""}.si-floppy-o::before{content:""}.si-square::before{content:""}.si-bars::before{content:""}.si-list-ul::before{content:""}.si-list-ol::before{content:""}.si-strikethrough::before{content:""}.si-underline::before{content:""}.si-table::before{content:""}.si-magic::before{content:""}.si-truck::before{content:""}.si-pinterest::before{content:""}.si-pinterest-square::before{content:""}.si-google-plus-square::before{content:""}.si-google-plus::before{content:""}.si-money::before{content:""}.si-caret-down::before{content:""}.si-caret-up::before{content:""}.si-caret-left::before{content:""}.si-caret-right::before{content:""}.si-columns::before{content:""}.si-sort::before{content:""}.si-sort-desc::before{content:""}.si-sort-asc::before{content:""}.si-envelope::before{content:""}.si-linkedin::before{content:""}.si-rotate-left::before{content:""}.si-gavel::before{content:""}.si-dashboard::before{content:""}.si-comment-o::before{content:""}.si-comments-o::before{content:""}.si-bolt::before{content:""}.si-sitemap::before{content:""}.si-umbrella::before{content:""}.si-clipboard::before{content:""}.si-lightbulb-o::before{content:""}.si-exchange::before{content:""}.si-cloud-download::before{content:""}.si-cloud-upload::before{content:""}.si-user-md::before{content:""}.si-stethoscope::before{content:""}.si-suitcase::before{content:""}.si-bell::before{content:""}.si-coffee::before{content:""}.si-cutlery::before{content:""}.si-file-text-o::before{content:""}.si-building-o::before{content:""}.si-hospital-o::before{content:""}.si-ambulance::before{content:""}.si-medkit::before{content:""}.si-fighter-jet::before{content:""}.si-beer::before{content:""}.si-h-square::before{content:""}.si-plus-square::before{content:""}.si-angle-double-left::before{content:""}.si-angle-double-right::before{content:""}.si-angle-double-up::before{content:""}.si-angle-double-down::before{content:""}.si-angle-left::before{content:""}.si-angle-right::before{content:""}.si-angle-up::before{content:""}.si-angle-down::before{content:""}.si-desktop::before{content:""}.si-laptop::before{content:""}.si-tablet::before{content:""}.si-mobile::before{content:""}.si-circle-o::before{content:""}.si-quote-left::before{content:""}.si-quote-right::before{content:""}.si-spinner::before{content:""}.si-circle::before{content:""}.si-mail-reply::before{content:""}.si-github-alt::before{content:""}.si-folder-o::before{content:""}.si-folder-open-o::before{content:""}.si-smile-o::before{content:""}.si-frown-o::before{content:""}.si-meh-o::before{content:""}.si-gamepad::before{content:""}.si-keyboard-o::before{content:""}.si-flag-o::before{content:""}.si-flag-checkered::before{content:""}.si-terminal::before{content:""}.si-code::before{content:""}.si-mail-reply-all::before{content:""}.si-star-half-empty::before{content:""}.si-location-arrow::before{content:""}.si-crop::before{content:""}.si-code-fork::before{content:""}.si-chain-broken::before{content:""}.si-question::before{content:""}.si-info::before{content:""}.si-exclamation::before{content:""}.si-superscript::before{content:""}.si-subscript::before{content:""}.si-eraser::before{content:""}.si-puzzle-piece::before{content:""}.si-microphone::before{content:""}.si-microphone-slash::before{content:""}.si-shield::before{content:""}.si-calendar-o::before{content:""}.si-fire-extinguisher::before{content:""}.si-rocket::before{content:""}.si-maxcdn::before{content:""}.si-chevron-circle-left::before{content:""}.si-chevron-circle-right::before{content:""}.si-chevron-circle-up::before{content:""}.si-chevron-circle-down::before{content:""}.si-html5::before{content:""}.si-css3::before{content:""}.si-anchor::before{content:""}.si-unlock-alt::before{content:""}.si-bullseye::before{content:""}.si-ellipsis-h::before{content:""}.si-ellipsis-v::before{content:""}.si-rss-square::before{content:""}.si-play-circle::before{content:""}.si-ticket::before{content:""}.si-minus-square::before{content:""}.si-minus-square-o::before{content:""}.si-level-up::before{content:""}.si-level-down::before{content:""}.si-check-square::before{content:""}.si-pencil-square::before{content:""}.si-external-link-square::before{content:""}.si-share-square::before{content:""}.si-compass::before{content:""}.si-caret-square-o-down::before{content:""}.si-caret-square-o-up::before{content:""}.si-caret-square-o-right::before{content:""}.si-eur::before{content:""}.si-gbp::before{content:""}.si-dollar::before{content:""}.si-inr::before{content:""}.si-cny::before{content:""}.si-rouble::before{content:""}.si-krw::before{content:""}.si-bitcoin::before{content:""}.si-file::before{content:""}.si-file-text::before{content:""}.si-sort-alpha-asc::before{content:""}.si-sort-alpha-desc::before{content:""}.si-sort-amount-asc::before{content:""}.si-sort-amount-desc::before{content:""}.si-sort-numeric-asc::before{content:""}.si-sort-numeric-desc::before{content:""}.si-thumbs-up::before{content:""}.si-thumbs-down::before{content:""}.si-youtube-square::before{content:""}.si-youtube::before{content:""}.si-xing::before{content:""}.si-xing-square::before{content:""}.si-youtube-play::before{content:""}.si-dropbox::before{content:""}.si-stack-overflow::before{content:""}.si-instagram::before{content:""}.si-flickr::before{content:""}.si-adn::before{content:""}.si-bitbucket::before{content:""}.si-bitbucket-square::before{content:""}.si-tumblr::before{content:""}.si-tumblr-square::before{content:""}.si-long-arrow-down::before{content:""}.si-long-arrow-up::before{content:""}.si-long-arrow-left::before{content:""}.si-long-arrow-right::before{content:""}.si-apple::before{content:""}.si-windows::before{content:""}.si-android::before{content:""}.si-linux::before{content:""}.si-dribbble::before{content:""}.si-skype::before{content:""}.si-foursquare::before{content:""}.si-trello::before{content:""}.si-female::before{content:""}.si-male::before{content:""}.si-gittip::before{content:""}.si-sun-o::before{content:""}.si-moon-o::before{content:""}.si-archive::before{content:""}.si-bug::before{content:""}.si-vk::before{content:""}.si-weibo::before{content:""}.si-renren::before{content:""}.si-pagelines::before{content:""}.si-stack-exchange::before{content:""}.si-arrow-circle-o-right::before{content:""}.si-arrow-circle-o-left::before{content:""}.si-caret-square-o-left::before{content:""}.si-dot-circle-o::before{content:""}.si-wheelchair::before{content:""}.si-vimeo-square::before{content:""}.si-try::before{content:""}.si-plus-square-o::before{content:""}.si-space-shuttle::before{content:""}.si-slack::before{content:""}.si-envelope-square::before{content:""}.si-wordpress::before{content:""}.si-openid::before{content:""}.si-bank::before{content:""}.si-graduation-cap::before{content:""}.si-yahoo::before{content:""}.si-google::before{content:""}.si-reddit::before{content:""}.si-reddit-square::before{content:""}.si-stumbleupon-circle::before{content:""}.si-stumbleupon::before{content:""}.si-delicious::before{content:""}.si-digg::before{content:""}.si-pied-piper::before{content:""}.si-pied-piper-alt::before{content:""}.si-drupal::before{content:""}.si-joomla::before{content:""}.si-language::before{content:""}.si-fax::before{content:""}.si-building::before{content:""}.si-child::before{content:""}.si-paw::before{content:""}.si-spoon::before{content:""}.si-cube::before{content:""}.si-cubes::before{content:""}.si-behance::before{content:""}.si-behance-square::before{content:""}.si-steam::before{content:""}.si-steam-square::before{content:""}.si-recycle::before{content:""}.si-automobile::before{content:""}.si-cab::before{content:""}.si-tree::before{content:""}.si-spotify::before{content:""}.si-deviantart::before{content:""}.si-soundcloud::before{content:""}.si-database::before{content:""}.si-file-pdf-o::before{content:""}.si-file-word-o::before{content:""}.si-file-excel-o::before{content:""}.si-file-powerpoint-o::before{content:""}.si-file-image-o::before{content:""}.si-file-archive-o::before{content:""}.si-file-audio-o::before{content:""}.si-file-movie-o::before{content:""}.si-file-code-o::before{content:""}.si-vine::before{content:""}.si-codepen::before{content:""}.si-jsfiddle::before{content:""}.si-life-bouy::before{content:""}.si-circle-o-notch::before{content:""}.si-ra::before{content:""}.si-empire::before{content:""}.si-git-square::before{content:""}.si-git::before{content:""}.si-hacker-news::before{content:""}.si-tencent-weibo::before{content:""}.si-qq::before{content:""}.si-wechat::before{content:""}.si-paper-plane::before{content:""}.si-paper-plane-o::before{content:""}.si-history::before{content:""}.si-circle-thin::before{content:""}.si-header::before{content:""}.si-paragraph::before{content:""}.si-sliders::before{content:""}.si-share-alt::before{content:""}.si-share-alt-square::before{content:""}.si-bomb::before{content:""}.si-futbol-o::before{content:""}.si-tty::before{content:""}.si-binoculars::before{content:""}.si-plug::before{content:""}.si-slideshare::before{content:""}.si-twitch::before{content:""}.si-yelp::before{content:""}.si-newspaper-o::before{content:""}.si-wifi::before{content:""}.si-calculator::before{content:""}.si-paypal::before{content:""}.si-google-wallet::before{content:""}.si-cc-visa::before{content:""}.si-cc-mastercard::before{content:""}.si-cc-discover::before{content:""}.si-cc-amex::before{content:""}.si-cc-paypal::before{content:""}.si-cc-stripe::before{content:""}.si-bell-slash::before{content:""}.si-bell-slash-o::before{content:""}.si-trash::before{content:""}.si-copyright::before{content:""}.si-at::before{content:""}.si-eyedropper::before{content:""}.si-paint-brush::before{content:""}.si-birthday-cake::before{content:""}.si-area-chart::before{content:""}.si-pie-chart::before{content:""}.si-line-chart::before{content:""}.si-lastfm::before{content:""}.si-lastfm-square::before{content:""}.si-toggle-off::before{content:""}.si-toggle-on::before{content:""}.si-bicycle::before{content:""}.si-bus::before{content:""}.si-ioxhost::before{content:""}.si-angellist::before{content:""}.si-cc::before{content:""}.si-ils::before{content:""}.si-meanpath::before{content:""}.si-buysellads::before{content:""}.si-connectdevelop::before{content:""}.si-dashcube::before{content:""}.si-forumbee::before{content:""}.si-leanpub::before{content:""}.si-sellsy::before{content:""}.si-shirtsinbulk::before{content:""}.si-simplybuilt::before{content:""}.si-skyatlas::before{content:""}.si-cart-plus::before{content:""}.si-cart-arrow-down::before{content:""}.si-diamond::before{content:""}.si-ship::before{content:""}.si-user-secret::before{content:""}.si-motorcycle::before{content:""}.si-street-view::before{content:""}.si-heartbeat::before{content:""}.si-venus::before{content:""}.si-mars::before{content:""}.si-mercury::before{content:""}.si-intersex::before{content:""}.si-transgender-alt::before{content:""}.si-venus-double::before{content:""}.si-mars-double::before{content:""}.si-venus-mars::before{content:""}.si-mars-stroke::before{content:""}.si-mars-stroke-v::before{content:""}.si-mars-stroke-h::before{content:""}.si-neuter::before{content:""}.si-genderless::before{content:""}.si-facebook-official::before{content:""}.si-pinterest-p::before{content:""}.si-whatsapp::before{content:""}.si-server::before{content:""}.si-user-plus::before{content:""}.si-user-times::before{content:""}.si-bed::before{content:""}.si-viacoin::before{content:""}.si-train::before{content:""}.si-subway::before{content:""}.si-medium::before{content:""}.si-y-combinator::before{content:""}.si-optin-monster::before{content:""}.si-opencart::before{content:""}.si-expeditedssl::before{content:""}.si-battery-4::before{content:""}.si-battery-3::before{content:""}.si-battery-2::before{content:""}.si-battery-1::before{content:""}.si-battery-0::before{content:""}.si-mouse-pointer::before{content:""}.si-i-cursor::before{content:""}.si-object-group::before{content:""}.si-object-ungroup::before{content:""}.si-sticky-note::before{content:""}.si-sticky-note-o::before{content:""}.si-cc-jcb::before{content:""}.si-cc-diners-club::before{content:""}.si-clone::before{content:""}.si-balance-scale::before{content:""}.si-hourglass-o::before{content:""}.si-hourglass-1::before{content:""}.si-hourglass-2::before{content:""}.si-hourglass-3::before{content:""}.si-hourglass::before{content:""}.si-hand-grab-o::before{content:""}.si-hand-paper-o::before{content:""}.si-hand-scissors-o::before{content:""}.si-hand-lizard-o::before{content:""}.si-hand-spock-o::before{content:""}.si-hand-pointer-o::before{content:""}.si-hand-peace-o::before{content:""}.si-trademark::before{content:""}.si-registered::before{content:""}.si-creative-commons::before{content:""}.si-gg::before{content:""}.si-gg-circle::before{content:""}.si-tripadvisor::before{content:""}.si-odnoklassniki::before{content:""}.si-odnoklassniki-square::before{content:""}.si-get-pocket::before{content:""}.si-wikipedia-w::before{content:""}.si-safari::before{content:""}.si-chrome::before{content:""}.si-firefox::before{content:""}.si-opera::before{content:""}.si-internet-explorer::before{content:""}.si-television::before{content:""}.si-contao::before{content:""}.si-500px::before{content:""}.si-amazon::before{content:""}.si-calendar-plus-o::before{content:""}.si-calendar-minus-o::before{content:""}.si-calendar-times-o::before{content:""}.si-calendar-check-o::before{content:""}.si-industry::before{content:""}.si-map-pin::before{content:""}.si-map-signs::before{content:""}.si-map-o::before{content:""}.si-map::before{content:""}.si-commenting::before{content:""}.si-commenting-o::before{content:""}.si-houzz::before{content:""}.si-vimeo::before{content:""}.si-black-tie::before{content:""}.si-fonticons::before{content:""}.si-lg{font-size:1.33333333em;line-height:0.75em;vertical-align:-15%}.si-2x{font-size:2em}.si-3x{font-size:3em}.si-4x{font-size:4em}.si-5x{font-size:5em}.si-fw{text-align:center;width:1.28571429em}.si-border{border-radius:.1em;padding:.2em .25em .15em;border:solid 0.08em}.si-pull-left{float:left;margin-right:.3em}.si-pull-right{float:right;margin-left:.3em}.si-rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.si-rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.si-rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.si-flip-horizontal{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.si-flip-vertical{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.si-inverse{-webkit-filter:invert(100%);filter:invert(100%)}.si-spin{-webkit-animation:si-animation-spin 2s infinite linear;-moz-animation:si-animation-spin 2s infinite linear;animation:si-animation-spin 2s infinite linear}.si-pulse{-webkit-animation:si-animation-spin 1s infinite steps(8);-moz-animation:si-animation-spin 1s infinite steps(8);animation:si-animation-spin 1s infinite steps(8)}.si-ul{padding-left:0;list-style-type:none;margin-left:2.14285714em}.si-ul>li{position:relative}.si-ul>li .si-li{text-align:center;position:absolute;left:-2.14285714em;width:2.14285714em;top:0.14285714em}.si-ul>li .si-li .si-lg{left:-1.85714286em}.si-stack{width:2em;height:2em;line-height:2em;vertical-align:middle;position:relative;display:inline-block}.si-stack .si-stack-1x,.si-stack .si-stack-2x{left:0;width:100%;text-align:center;position:absolute}.si-stack .si-stack-1x{line-height:inherit}.si-stack .si-stack-2x{font-size:2em}@-webkit-keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-moz-keyframes si-animation-spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@keyframes si-animation-spin{0%{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);-o-transform:rotate(359deg);transform:rotate(359deg)}} -/*# sourceMappingURL=short-classes.min.css.map */ \ No newline at end of file diff --git a/src/vendor/websharks/wp-i18n-tools/CHANGELOG.md b/src/vendor/websharks/wp-i18n-tools/CHANGELOG.md new file mode 100644 index 0000000..603d033 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/CHANGELOG.md @@ -0,0 +1,3 @@ +## v15xxxx + +- Initial release. diff --git a/src/vendor/websharks/wp-i18n-tools/README.md b/src/vendor/websharks/wp-i18n-tools/README.md new file mode 100644 index 0000000..aa26994 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/README.md @@ -0,0 +1,13 @@ +## WP i18n Tools + +Modified slightly by WebSharks, Inc. + +#### Differences from original: + +- Adding shebang line to the top of `makepot.php` +- Setting executable bit on `makepot.php` and preserving that with Git. +- Updated to support PHP/translations embedded into `.js` files too. +- Disabling line wrapping from final output to ensure better consistency. +- Adding support for plugin directories that use a `plugin.php` file instead of `[slug].php`. +- Adding `composer.json` and package on Packagist.org. +- Auto-exclude `(?:.+?/)?vendor/.*` in themes/plugins. diff --git a/src/vendor/websharks/wp-i18n-tools/add-textdomain.php b/src/vendor/websharks/wp-i18n-tools/add-textdomain.php new file mode 100644 index 0000000..e31d3c1 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/add-textdomain.php @@ -0,0 +1,110 @@ +funcs = array_keys( $makepot->rules ); + } + + function usage() { + $usage = "Usage: php add-textdomain.php [-i] \n\nAdds the string as a last argument to all i18n function calls in \nand prints the modified php file on standard output.\n\nOptions:\n -i Modifies the PHP file in place, instead of printing it to standard output.\n"; + fwrite(STDERR, $usage); + exit(1); + } + + function process_token($token_text, $inplace) { + if ($inplace) + $this->modified_contents .= $token_text; + else + echo $token_text; + } + + + function process_file($domain, $source_filename, $inplace) { + + $this->modified_contents = ''; + $domain = addslashes($domain); + + $source = file_get_contents($source_filename); + $tokens = token_get_all($source); + + $in_func = false; + $args_started = false; + $parens_balance = 0; + $found_domain = false; + + foreach($tokens as $token) { + $string_success = false; + if (is_array($token)) { + list($id, $text) = $token; + if (T_STRING == $id && in_array($text, $this->funcs)) { + $in_func = true; + $parens_balance = 0; + $args_started = false; + $found_domain = false; + } elseif (T_CONSTANT_ENCAPSED_STRING == $id && ("'$domain'" == $text || "\"$domain\"" == $text)) { + if ($in_func && $args_started) { + $found_domain = true; + } + } + $token = $text; + } elseif ('(' == $token){ + $args_started = true; + ++$parens_balance; + } elseif (')' == $token) { + --$parens_balance; + if ($in_func && 0 == $parens_balance) { + $token = $found_domain? ')' : ", '$domain')"; + $in_func = false; + $args_started = false; + $found_domain = false; + } + } + $this->process_token($token, $inplace); + } + + if ($inplace) { + $f = fopen($source_filename, 'w'); + fwrite($f, $this->modified_contents); + fclose($f); + } + } +} + + +// run the CLI only if the file +// wasn't included +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + $adddomain = new AddTextdomain; + + if (!isset($argv[1]) || !isset($argv[2])) { + $adddomain->usage(); + } + + $inplace = false; + if ('-i' == $argv[1]) { + $inplace = true; + if (!isset($argv[3])) $adddomain->usage(); + array_shift($argv); + } + + $adddomain->process_file($argv[1], $argv[2], $inplace); +} + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/cron-svn-pots.php b/src/vendor/websharks/wp-i18n-tools/cron-svn-pots.php new file mode 100644 index 0000000..aaf015d --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/cron-svn-pots.php @@ -0,0 +1,117 @@ +&1", $exit_code ); + $output = ob_get_contents(); + ob_end_clean(); + if ( $exit_code != 0 ) { + echo "ERROR:\t$command\nCODE:\t$exit_code\nOUTPUT:\n"; + echo $output."\n"; + } else { + echo "OK:\t$command\n"; + } + return $exit_code; +} + + +$options = getopt( 'c:p:m:n:sa:b:u:w:df' ); +if ( empty( $options ) ) { +?> + -s No branch/version directories, it's all flat + -c Application svn checkout + -p POT svn checkout + -m MakePOT project + -n POT filename + -a Relative path of application inside version dir in -c + -b Relative patch of POT dir inside version dir in -p + -u SVN username (optional) + -w SVN password (optional) + -d Dry-run + -f Fast - do not update checkouts + 16; + $target = $exists ? $pot : $version; + if ( !$exists || $real_differences ) { + preg_match( '/Revision:\s+(\d+)/', `svn info $application_path`, $matches ); + $logmsg = isset( $matches[1] ) && intval( $matches[1] )? "POT, generated from r".intval( $matches[1] ) : 'Automatic POT update'; + $command = "$svn ci $target --non-interactive --message='$logmsg'"; + if ( !$dry_run ) + silent_system( $command ); + else + echo "CMD:\t$command\n"; + } else { + silent_system( "$svn revert $target" ); + } +} diff --git a/src/vendor/websharks/wp-i18n-tools/extract/ExtractTest.php b/src/vendor/websharks/wp-i18n-tools/extract/ExtractTest.php new file mode 100644 index 0000000..f4c43be --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/extract/ExtractTest.php @@ -0,0 +1,160 @@ +extractor = new StringExtractor; + $this->extractor->rules = array( + '__' => array('string'), + ); + } + + function test_with_just_a_string() { + $expected = new Translation_Entry( array( 'singular' => 'baba', 'references' => array('baba.php:1') ) ); + $result = $this->extractor->extract_entries('', 'baba.php' ); + $this->assertEquals( $expected, $result->entries['baba'] ); + } + + function test_entry_from_call_simple() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba' ) ) ); + } + + function test_entry_from_call_nonexisting_function() { + $entry = $this->extractor->entry_from_call( array( 'name' => 'f', 'args' => array('baba') ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_too_few_args() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array() ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_non_expected_null_arg() { + $this->extractor->rules = array( '_nx' => array( 'singular', 'plural', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_nx', 'args' => array('%s baba', null, 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, null ); + } + + function test_entry_from_call_more_args_should_be_ok() { + $this->extractor->rules = array( '__' => array('string') ); + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba', 5, 'pijo', null) ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba' ) ) ); + } + + + function test_entry_from_call_context() { + $this->extractor->rules = array( '_x' => array( 'string', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_x', 'args' => array('baba', 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'context' => 'noun' ) ) ); + } + + function test_entry_from_call_plural() { + $this->extractor->rules = array( '_n' => array( 'singular', 'plural' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_n', 'args' => array('%s baba', '%s babas') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '%s baba', 'plural' => '%s babas' ) ) ); + } + + function test_entry_from_call_plural_and_context() { + $this->extractor->rules = array( '_nx' => array( 'singular', 'plural', 'context' ) ); + $entry = $this->extractor->entry_from_call( array( 'name' => '_nx', 'args' => array('%s baba', '%s babas', 'noun') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '%s baba', 'plural' => '%s babas', 'context' => 'noun' ) ) ); + } + + function test_entry_from_call_extracted_comment() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba'), 'comment' => 'translators: give me back my pants!' ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'extracted_comments' => "translators: give me back my pants!\n" ) ) ); + } + + function test_entry_from_call_line_number() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('baba'), 'line' => 10 ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => 'baba', 'references' => array('baba.php:10') ) ) ); + } + + function test_entry_from_call_zero() { + $entry = $this->extractor->entry_from_call( array( 'name' => '__', 'args' => array('0') ), 'baba.php' ); + $this->assertEquals( $entry, new Translation_Entry( array( 'singular' => '0' ) ) ); + } + + function test_entry_from_call_multiple() { + $this->extractor->rules = array( 'c' => array( 'string', 'singular', 'plural' ) ); + $entries = $this->extractor->entry_from_call( array( 'name' => 'c', 'args' => array('baba', 'dyado', 'dyados') ), 'baba.php' ); + $this->assertEquals( array( + new Translation_Entry( array( 'singular' => 'baba' ) ), new Translation_Entry( array( 'singular' => 'dyado', 'plural' => 'dyados' ) ) ), $entries ); + } + + function test_entry_from_call_multiple_first_plural_then_two_strings() { + $this->extractor->rules = array( 'c' => array( 'singular', 'plural', null, 'string', 'string' ) ); + $entries = $this->extractor->entry_from_call( array( 'name' => 'c', 'args' => array('dyado', 'dyados', 'baba', 'foo', 'bar') ), 'baba.php' ); + $this->assertEquals( array( + new Translation_Entry( array( 'singular' => 'dyado', 'plural' => 'dyados' ) ), + new Translation_Entry( array( 'singular' => 'foo' ) ), + new Translation_Entry( array( 'singular' => 'bar' ) ) ), $entries ); + } + + function test_find_function_calls_one_arg_literal() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( 'baba' ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_one_arg_zero() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( '0' ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_one_arg_non_literal() { + $this->assertEquals( array( array( 'name' => '__', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('__'), '' ) ); + } + + function test_find_function_calls_shouldnt_be_mistaken_by_a_class() { + $this->assertEquals( array(), $this->extractor->find_function_calls( array('__'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba" ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba", null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( "baba", null ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( array( array( 'name' => 'f', 'args' => array( null, "baba" ), 'line' => 1 ) ), $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'assertEquals( + array( array( 'name' => 'f', 'args' => array( 'baba' ), 'line' => 1, 'comment' => 'Translators: let your ears fly!' ) ), + $this->extractor->find_function_calls( array('f'), 'rules = $rules; + } + + function extract_from_directory( $dir, $excludes = array(), $includes = array(), $prefix = '' ) { + $old_cwd = getcwd(); + chdir( $dir ); + $translations = new Translations; + $file_names = (array) scandir( '.' ); + foreach ( $file_names as $file_name ) { + if ( '.' == $file_name || '..' == $file_name ) continue; + if ( preg_match( '/\.(?:php|js)$/', $file_name ) && $this->does_file_name_match( $prefix . $file_name, $excludes, $includes ) ) { + $translations->merge_originals_with( $this->extract_from_file( $file_name, $prefix ) ); + } + if ( is_dir( $file_name ) ) { + $translations->merge_originals_with( $this->extract_from_directory( $file_name, $excludes, $includes, $prefix . $file_name . '/' ) ); + } + } + chdir( $old_cwd ); + return $translations; + } + + function extract_from_file( $file_name, $prefix ) { + $code = file_get_contents( $file_name ); + return $this->extract_entries( $code, $prefix . $file_name ); + } + + function does_file_name_match( $path, $excludes, $includes ) { + if ( $includes ) { + $matched_any_include = false; + foreach( $includes as $include ) { + if ( preg_match( '|^'.$include.'$|', $path ) ) { + $matched_any_include = true; + break; + } + } + if ( !$matched_any_include ) return false; + } + if ( $excludes ) { + foreach( $excludes as $exclude ) { + if ( preg_match( '|^'.$exclude.'$|', $path ) ) { + return false; + } + } + } + return true; + } + + function entry_from_call( $call, $file_name ) { + $rule = isset( $this->rules[$call['name']] )? $this->rules[$call['name']] : null; + if ( !$rule ) return null; + $entry = new Translation_Entry; + $multiple = array(); + $complete = false; + for( $i = 0; $i < count( $rule ); ++$i ) { + if ( $rule[$i] && ( !isset( $call['args'][$i] ) || !is_string( $call['args'][$i] ) || '' == $call['args'][$i] ) ) return false; + switch( $rule[$i] ) { + case 'string': + if ( $complete ) { + $multiple[] = $entry; + $entry = new Translation_Entry; + $complete = false; + } + $entry->singular = $call['args'][$i]; + $complete = true; + break; + case 'singular': + if ( $complete ) { + $multiple[] = $entry; + $entry = new Translation_Entry; + $complete = false; + } + $entry->singular = $call['args'][$i]; + $entry->is_plural = true; + break; + case 'plural': + $entry->plural = $call['args'][$i]; + $entry->is_plural = true; + $complete = true; + break; + case 'context': + $entry->context = $call['args'][$i]; + foreach( $multiple as &$single_entry ) { + $single_entry->context = $entry->context; + } + break; + } + } + if ( isset( $call['line'] ) && $call['line'] ) { + $references = array( $file_name . ':' . $call['line'] ); + $entry->references = $references; + foreach( $multiple as &$single_entry ) { + $single_entry->references = $references; + } + } + if ( isset( $call['comment'] ) && $call['comment'] ) { + $comments = rtrim( $call['comment'] ) . "\n"; + $entry->extracted_comments = $comments; + foreach( $multiple as &$single_entry ) { + $single_entry->extracted_comments = $comments; + } + } + if ( $multiple && $entry ) { + $multiple[] = $entry; + return $multiple; + } + + return $entry; + } + + function extract_entries( $code, $file_name ) { + $translations = new Translations; + $function_calls = $this->find_function_calls( array_keys( $this->rules ), $code ); + foreach( $function_calls as $call ) { + $entry = $this->entry_from_call( $call, $file_name ); + if ( is_array( $entry ) ) + foreach( $entry as $single_entry ) + $translations->add_entry_or_merge( $single_entry ); + elseif ( $entry) + $translations->add_entry_or_merge( $entry ); + } + return $translations; + } + + /** + * Finds all function calls in $code and returns an array with an associative array for each function: + * - name - name of the function + * - args - array for the function arguments. Each string literal is represented by itself, other arguments are represented by null. + * - line - line number + */ + function find_function_calls( $function_names, $code ) { + $tokens = token_get_all( $code ); + $function_calls = array(); + $latest_comment = false; + $in_func = false; + foreach( $tokens as $token ) { + $id = $text = null; + if ( is_array( $token ) ) list( $id, $text, $line ) = $token; + if ( T_WHITESPACE == $id ) continue; + if ( T_STRING == $id && in_array( $text, $function_names ) && !$in_func ) { + $in_func = true; + $paren_level = -1; + $args = array(); + $func_name = $text; + $func_line = $line; + $func_comment = $latest_comment? $latest_comment : ''; + + $just_got_into_func = true; + $latest_comment = false; + continue; + } + if ( T_COMMENT == $id ) { + $text = trim( preg_replace( '%^/\*|//%', '', preg_replace( '%\*/$%', '', $text ) ) ); + if ( 0 === stripos( $text, $this->comment_prefix ) ) { + $latest_comment = $text; + } + } + if ( !$in_func ) continue; + if ( '(' == $token ) { + $paren_level++; + if ( 0 == $paren_level ) { // start of first argument + $just_got_into_func = false; + $current_argument = null; + $current_argument_is_just_literal = true; + } + continue; + } + if ( $just_got_into_func ) { + // there wasn't a opening paren just after the function name -- this means it is not a function + $in_func = false; + $just_got_into_func = false; + } + if ( ')' == $token ) { + if ( 0 == $paren_level ) { + $in_func = false; + $args[] = $current_argument; + $call = array( 'name' => $func_name, 'args' => $args, 'line' => $func_line ); + if ( $func_comment ) $call['comment'] = $func_comment; + $function_calls[] = $call; + } + $paren_level--; + continue; + } + if ( ',' == $token && 0 == $paren_level ) { + $args[] = $current_argument; + $current_argument = null; + $current_argument_is_just_literal = true; + continue; + } + if ( T_CONSTANT_ENCAPSED_STRING == $id && $current_argument_is_just_literal ) { + // we can use eval safely, because we are sure $text is just a string literal + eval('$current_argument = '.$text.';' ); + continue; + } + $current_argument_is_just_literal = false; + $current_argument = null; + } + return $function_calls; + } +} diff --git a/src/vendor/websharks/wp-i18n-tools/importers-i18n.php b/src/vendor/websharks/wp-i18n-tools/importers-i18n.php new file mode 100644 index 0000000..d455036 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/importers-i18n.php @@ -0,0 +1,195 @@ +importers = array_map( create_function( '$x', 'return $x."-importer";' ), $this->importers_slugs); + } + + function s( $importer, $text ) { + return str_replace( array( '%importer%', '%trunk%' ), array( $importer, "$importer/trunk" ), $text ); + } + + function call_on_all( $command ) { + foreach( $this->importers as $importer ) { + system( $this->s( $importer, $command ) ); + } + } + + function ls() { + $this->call_on_all( "ls %trunk%" ); + } + + function command( $command ) { + $this->call_on_all( $command ); + } + + + function checkout() { + $this->call_on_all( "svn checkout --username=$this->username http://plugins.svn.wordpress.org/%importer%" ); + } + + function update() { + $this->call_on_all( "svn up %importer%" ); + } + + function rename_main_file() { + $this->call_on_all( "svn mv %importer%/trunk/*.php %importer%/trunk/%importer%.php" ); + } + + function link( $target ) { + $this->call_on_all( "ln -s `pwd`/%importer%/trunk $target/%importer%" ); + } + + function add_load_plugin_textdomain_call() { + foreach( $this->importers as $importer ) { + $importer_underscore = str_replace( '-', '_', $importer ); + $main_file_name = "$importer/trunk/$importer.php"; + $main_file = file_get_contents( $main_file_name ); + $main_file .= " +function {$importer_underscore}_init() { + load_plugin_textdomain( '$importer', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); +} +add_action( 'init', '{$importer_underscore}_init' ); +"; + file_put_contents( $main_file_name, $main_file ); + } + } + + function make_languages_dir() { + $this->call_on_all( "svn mkdir %importer%/trunk/languages" ); + } + + function generate_pot() { + foreach( $this->importers as $importer ) { + $old_cwd = getcwd(); + chdir( "$importer/trunk/languages" ); + system( "php ".dirname(__FILE__)."/makepot.php wp-plugin .." ); + chdir( $old_cwd ); + } + } + + + function diff() { + $this->call_on_all( "svn diff %trunk%" ); + } + + function st() { + $this->call_on_all( "svn st %trunk%" ); + } + + function svn_add_missing() { + $this->call_on_all( "svn add %trunk%/*" ); + $this->call_on_all( "svn add %trunk%/languages/*" ); + } + + function lint() { + $this->call_on_all( "php -l %importer% %trunk%/%importer%.php" ); + } + + + function add_textdomain() { + $this->call_on_all( "php ".dirname(__FILE__)."/add-textdomain.php -i %importer% %trunk%/%importer%.php" ); + } + + function sync() { + $this->call_on_all( "cp -R %trunk% $this->plugins_dir/%importer%" ); + } + + function commit($message) { + $this->call_on_all( "svn ci %trunk% -m ".escapeshellarg($message) ); + } + + function update_translations() { + require_once $this->glotpress_source_dir . '/locales/locales.php'; + foreach( $this->importers as $importer ) { + $project_details_json = file_get_contents( $this->s( $importer, $this->glotpress_api_url ) ); + $project_details = json_decode( $project_details_json ); + if ( !is_object( $project_details ) ) { + echo "Couldn't get project JSON from GlotPress for $importer.\n"; + continue; + } + foreach( $project_details->translation_sets as $set ) { + $locale = GP_Locales::by_slug( $set->locale ); + if ( !$locale->wp_locale ) { + echo "Locale '$set->locale' doesn't have WordPress equivalent.\n"; + continue; + } + $po_file = file_get_contents( $this->s( $importer, $this->glotpress_url ) . "/$locale->slug/$set->slug/export-translations" ); + if ( !$po_file ) { + echo "Couldn't download translation for '$importer' in '$set->locale'.\n"; + continue; + } + $po_path = $this->s( $importer, "%trunk%/languages/$locale->wp_locale.po" ); + $mo_path = str_replace( '.po', '.mo', $po_path ); + file_put_contents( $po_path, $po_file ); + $msgfmt_output = `msgfmt --statistics $po_path -o $mo_path 2>&1`; + preg_match( '/(\d+) translated messages(?:\.|, (\d+) untranslated messages)/', $msgfmt_output, $matches ); + if ( isset( $matches[2] ) ) { + $translated_percentage = $matches[1] / ( $matches[1] + $matches[2] ) * 100; + if ( $translated_percentage < $this->minimum_percentage ) { + unlink( $po_path ); + unlink( $mo_path ); + echo "Translation of '$importer' in '$locale->slug' has only $translated_percentage% translated, $this->minimum_percentage% are required.\n"; + } + } + } + } + } + + function create_glotpress_projects( $parent_project_path ) { + require_once $this->glotpress_source_dir . '/gp-load.php'; + require_once dirname(__FILE__) . '/makepot.php'; + $makepot = new MakePOT; + $parent_project = GP::$project->by_path( $parent_project_path ); + if ( !$parent_project ) { + echo "Couldn't find project with path $parent_project_path.\n"; + return; + } + foreach( $this->importers as $importer ) { + $source = $makepot->get_first_lines( $this->s( $importer, '%trunk%/%importer%.php' ), $makepot->max_header_lines); + if ( !GP::$project->by_path( "$parent_project_path/$importer") ) { + $importer_project = GP::$project->create_and_select(array( + 'name' => $makepot->get_addon_header('Plugin Name', $source), + 'slug' => $importer, + 'description' => $makepot->get_addon_header('Description', $source), + 'parent_project_id' => $parent_project->id, + )); + } else { + echo "Project $parent_project_path/$importer already exists.\n"; + } + if ( !GP::$project->by_path( "$parent_project_path/$importer/dev") ) { + $trunk_project = GP::$project->create_and_select(array( + 'name' => 'Development (trunk)', + 'slug' => 'dev', + 'description' => 'Development version of ' . $makepot->get_addon_header('Plugin Name', $source), + 'parent_project_id' => $importer_project->id, + 'source_url_template' => $this->s($importer, "http://plugins.trac.wordpress.org/browser/%importer%/trunk/%file%#L%line%"), + )); + } else { + echo "Project $parent_project_path/$importer/dev already exists.\n"; + } + } + } + + function import_glotpress_originals( $parent_project_path ) { + foreach( $this->importers as $importer ) { + system( $this->s( $importer, "php $this->glotpress_source_dir/scripts/import-originals.php -p $parent_project_path/%importer%/dev -f %trunk%/languages/%importer%.pot" ) ); + } + } + + function line_of_text( $line ) { + foreach( $this->importers as $importer ) { + echo $this->s( $importer, $line ) . "\n"; + } + } +} +$importers_i18n = new ImportersI18n; +call_user_func_array( array( &$importers_i18n, $argv[1] ), array_slice( $argv, 2 ) ); diff --git a/src/vendor/websharks/wp-i18n-tools/makepot.php b/src/vendor/websharks/wp-i18n-tools/makepot.php new file mode 100644 index 0000000..18e27e6 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/makepot.php @@ -0,0 +1,573 @@ +#!/usr/bin/env php + array('string'), + '__' => array('string'), + '_e' => array('string'), + '_c' => array('string'), + '_n' => array('singular', 'plural'), + '_n_noop' => array('singular', 'plural'), + '_nc' => array('singular', 'plural'), + '__ngettext' => array('singular', 'plural'), + '__ngettext_noop' => array('singular', 'plural'), + '_x' => array('string', 'context'), + '_ex' => array('string', 'context'), + '_nx' => array('singular', 'plural', null, 'context'), + '_nx_noop' => array('singular', 'plural', 'context'), + '_n_js' => array('singular', 'plural'), + '_nx_js' => array('singular', 'plural', 'context'), + 'esc_attr__' => array('string'), + 'esc_html__' => array('string'), + 'esc_attr_e' => array('string'), + 'esc_html_e' => array('string'), + 'esc_attr_x' => array('string', 'context'), + 'esc_html_x' => array('string', 'context'), + 'comments_number_link' => array('string', 'singular', 'plural'), + ); + + var $ms_files = array( 'ms-.*', '.*/ms-.*', '.*/my-.*', 'wp-activate\.php', 'wp-signup\.php', 'wp-admin/network\.php', 'wp-admin/includes/ms\.php', 'wp-admin/network/.*\.php', 'wp-admin/includes/class-wp-ms.*' ); + + var $temp_files = array(); + + var $meta = array( + 'default' => array( + 'from-code' => 'utf-8', + 'msgid-bugs-address' => 'http://wppolyglots.wordpress.com', + 'language' => 'php', + 'add-comments' => 'translators', + 'comments' => "Copyright (C) {year} {package-name}\nThis file is distributed under the same license as the {package-name} package.", + ), + 'generic' => array(), + 'wp-frontend' => array( + 'description' => 'Translation of frontend strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-admin' => array( + 'description' => 'Translation of site admin strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-network-admin' => array( + 'description' => 'Translation of network admin strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-core' => array( + 'description' => 'Translation of WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-ms' => array( + 'description' => 'Translation of multisite strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'wp-tz' => array( + 'description' => 'Translation of timezone strings in WordPress {version}', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress', + 'package-version' => '{version}', + ), + 'bb' => array( + 'description' => 'Translation of bbPress', + 'copyright-holder' => 'bbPress', + 'package-name' => 'bbPress', + ), + 'wp-plugin' => array( + 'description' => 'Translation of the WordPress plugin {name} {version} by {author}', + 'msgid-bugs-address' => 'http://wordpress.org/tag/{slug}', + 'copyright-holder' => '{author}', + 'package-name' => '{name}', + 'package-version' => '{version}', + ), + 'wp-theme' => array( + 'description' => 'Translation of the WordPress theme {name} {version} by {author}', + 'msgid-bugs-address' => 'http://wordpress.org/tags/{slug}', + 'copyright-holder' => '{author}', + 'package-name' => '{name}', + 'package-version' => '{version}', + 'comments' => 'Copyright (C) {year} {author}\nThis file is distributed under the same license as the {package-name} package.', + ), + 'bp' => array( + 'description' => 'Translation of BuddyPress', + 'copyright-holder' => 'BuddyPress', + 'package-name' => 'BuddyPress', + ), + 'glotpress' => array( + 'description' => 'Translation of GlotPress', + 'copyright-holder' => 'GlotPress', + 'package-name' => 'GlotPress', + ), + 'wporg-bb-forums' => array( + 'description' => 'WordPress.org International Forums', + 'copyright-holder' => 'WordPress', + 'package-name' => 'WordPress.org International Forums', + ), + 'rosetta' => array( + 'description' => 'Rosetta (.wordpress.org locale sites)', + 'copyright-holder' => 'WordPress', + 'package-name' => 'Rosetta', + ), + ); + + function __construct($deprecated = true) { + $this->extractor = new StringExtractor( $this->rules ); + } + + function __destruct() { + foreach ( $this->temp_files as $temp_file ) + unlink( $temp_file ); + } + + function tempnam( $file ) { + $tempnam = tempnam( sys_get_temp_dir(), $file ); + $this->temp_files[] = $tempnam; + return $tempnam; + } + + function realpath_missing($path) { + return realpath(dirname($path)).DIRECTORY_SEPARATOR.basename($path); + } + + function xgettext($project, $dir, $output_file, $placeholders = array(), $excludes = array(), $includes = array()) { + $meta = array_merge( $this->meta['default'], $this->meta[$project] ); + $placeholders = array_merge( $meta, $placeholders ); + $meta['output'] = $this->realpath_missing( $output_file ); + $placeholders['year'] = date( 'Y' ); + $placeholder_keys = array_map( create_function( '$x', 'return "{".$x."}";' ), array_keys( $placeholders ) ); + $placeholder_values = array_values( $placeholders ); + foreach($meta as $key => $value) { + $meta[$key] = str_replace($placeholder_keys, $placeholder_values, $value); + } + + $originals = $this->extractor->extract_from_directory( $dir, $excludes, $includes ); + $pot = new PO; + $pot->entries = $originals->entries; + + $pot->set_header( 'Project-Id-Version', $meta['package-name'].' '.$meta['package-version'] ); + $pot->set_header( 'Report-Msgid-Bugs-To', $meta['msgid-bugs-address'] ); + $pot->set_header( 'POT-Creation-Date', gmdate( 'Y-m-d H:i:s+00:00' ) ); + $pot->set_header( 'MIME-Version', '1.0' ); + $pot->set_header( 'Content-Type', 'text/plain; charset=UTF-8' ); + $pot->set_header( 'Content-Transfer-Encoding', '8bit' ); + $pot->set_header( 'PO-Revision-Date', date( 'Y') . '-MO-DA HO:MI+ZONE' ); + $pot->set_header( 'Last-Translator', 'FULL NAME ' ); + $pot->set_header( 'Language-Team', 'LANGUAGE ' ); + $pot->set_comment_before_headers( $meta['comments'] ); + $pot->export_to_file( $output_file ); + return true; + } + + function wp_generic($dir, $args) { + $defaults = array( + 'project' => 'wp-core', + 'output' => null, + 'default_output' => 'wordpress.pot', + 'includes' => array(), + 'excludes' => array_merge( + array('wp-admin/includes/continents-cities\.php', 'wp-content/themes/twenty.*', ), + $this->ms_files + ), + 'extract_not_gettexted' => false, + 'not_gettexted_files_filter' => false, + ); + $args = array_merge( $defaults, $args ); + extract( $args ); + $placeholders = array(); + if ( $wp_version = $this->wp_version( $dir ) ) + $placeholders['version'] = $wp_version; + else + return false; + $output = is_null( $output )? $default_output : $output; + $res = $this->xgettext( $project, $dir, $output, $placeholders, $excludes, $includes ); + if ( !$res ) return false; + + if ( $extract_not_gettexted ) { + $old_dir = getcwd(); + $output = realpath( $output ); + chdir( $dir ); + $php_files = NotGettexted::list_php_files('.'); + $php_files = array_filter( $php_files, $not_gettexted_files_filter ); + $not_gettexted = new NotGettexted; + $res = $not_gettexted->command_extract( $output, $php_files ); + chdir( $old_dir ); + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg( $output ); + system( "msguniq --use-first $output_shell -o $output_shell" ); + } + return $res; + } + + function wp_core($dir, $output) { + if ( file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + return $this->wp_generic( $dir, array( + 'project' => 'wp-core', 'output' => $output, + 'extract_not_gettexted' => true, + 'not_gettexted_files_filter' => array( &$this, 'is_not_ms_file' ), + ) ); + } + + function wp_frontend($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + return $this->wp_generic( $dir, array( + 'project' => 'wp-frontend', 'output' => $output, + 'includes' => array(), 'excludes' => array( 'wp-admin/.*', 'wp-content/themes/.*' ), + 'default_output' => 'wordpress.pot', + ) ); + } + + function wp_admin($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + $frontend_pot = $this->tempnam( 'frontend.pot' ); + if ( false === $frontend_pot ) return false; + + $frontend_result = $this->wp_frontend( $dir, $frontend_pot ); + if ( ! $frontend_result ) + return false; + + $result = $this->wp_generic( $dir, array( + 'project' => 'wp-admin', 'output' => $output, + 'includes' => array( 'wp-admin/.*' ), 'excludes' => array( 'wp-admin/includes/continents-cities\.php', 'wp-admin/network/.*', 'wp-admin/network.php' ), + 'default_output' => 'wordpress-admin.pot', + ) ); + + if ( ! $result ) + return false; + + $potextmeta = new PotExtMeta; + $result = $potextmeta->append( "$dir/wp-content/plugins/akismet/akismet.php", $output ); + if ( ! $result ) + return false; + $result = $potextmeta->append( "$dir/wp-content/plugins/hello.php", $output ); + if ( ! $result ) + return false; + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell"); + + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + $admin_pot = realpath( is_null( $output ) ? 'wordpress-admin.pot' : $output ); + system( "msgcat --more-than=1 --use-first $frontend_pot $admin_pot > $common_pot" ); + system( "msgcat -u --use-first $admin_pot $common_pot -o $admin_pot" ); + return true; + } + + function wp_network_admin($dir, $output) { + if ( ! file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + + $frontend_pot = $this->tempnam( 'frontend.pot' ); + if ( false === $frontend_pot ) return false; + + $frontend_result = $this->wp_frontend( $dir, $frontend_pot ); + if ( ! $frontend_result ) + return false; + + $admin_pot = $this->tempnam( 'admin.pot' ); + if ( false === $admin_pot ) return false; + + $admin_result = $this->wp_admin( $dir, $admin_pot ); + if ( ! $admin_result ) + return false; + + $result = $this->wp_generic( $dir, array( + 'project' => 'wp-network-admin', 'output' => $output, + 'includes' => array( 'wp-admin/network/.*', 'wp-admin/network.php' ), 'excludes' => array(), + 'default_output' => 'wordpress-admin-network.pot', + ) ); + + if ( ! $result ) { + return false; + } + + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + + $net_admin_pot = realpath( is_null( $output ) ? 'wordpress-network-admin.pot' : $output ); + system( "msgcat --more-than=1 --use-first $frontend_pot $admin_pot $net_admin_pot > $common_pot" ); + system( "msgcat -u --use-first $net_admin_pot $common_pot -o $net_admin_pot" ); + return true; + } + + function wp_ms($dir, $output) { + if ( file_exists( "$dir/wp-admin/user/about.php" ) ) return false; + if ( !is_file("$dir/wp-admin/ms-users.php") ) return false; + $core_pot = $this->tempnam( 'wordpress.pot' ); + if ( false === $core_pot ) return false; + $core_result = $this->wp_core( $dir, $core_pot ); + if ( ! $core_result ) + return false; + $ms_result = $this->wp_generic( $dir, array( + 'project' => 'wp-ms', 'output' => $output, + 'includes' => $this->ms_files, 'excludes' => array(), + 'default_output' => 'wordpress-ms.pot', + 'extract_not_gettexted' => true, + 'not_gettexted_files_filter' => array( &$this, 'is_ms_file' ), + ) ); + if ( !$ms_result ) { + return false; + } + $common_pot = $this->tempnam( 'common.pot' ); + if ( ! $common_pot ) + return false; + $ms_pot = realpath( is_null( $output )? 'wordpress-ms.pot' : $output ); + system( "msgcat --more-than=1 --use-first $core_pot $ms_pot > $common_pot" ); + system( "msgcat -u --use-first $ms_pot $common_pot -o $ms_pot" ); + return true; + } + + function wp_tz($dir, $output) { + $continents_path = 'wp-admin/includes/continents-cities.php'; + if ( !file_exists( "$dir/$continents_path" ) ) return false; + return $this->wp_generic( $dir, array( + 'project' => 'wp-tz', 'output' => $output, + 'includes' => array($continents_path), 'excludes' => array(), + 'default_output' => 'wordpress-continents-cities.pot', + ) ); + } + + function wp_version($dir) { + $version_php = $dir.'/wp-includes/version.php'; + if ( !is_readable( $version_php ) ) return false; + return preg_match( '/\$wp_version\s*=\s*\'(.*?)\';/', file_get_contents( $version_php ), $matches )? $matches[1] : false; + } + + + function mu($dir, $output) { + $placeholders = array(); + if (preg_match('/\$wpmu_version\s*=\s*\'(.*?)\';/', file_get_contents($dir.'/wp-includes/version.php'), $matches)) { + $placeholders['version'] = $matches[1]; + } + $output = is_null($output)? 'wordpress.pot' : $output; + return $this->xgettext('wp', $dir, $output, $placeholders); + } + + + function bb($dir, $output) { + $placeholders = array(); + $output = is_null($output)? 'bbpress.pot' : $output; + return $this->xgettext('bb', $dir, $output, $placeholders); + + } + + function get_first_lines($filename, $lines = 30) { + $extf = fopen($filename, 'r'); + if (!$extf) return false; + $first_lines = ''; + foreach(range(1, $lines) as $x) { + $line = fgets($extf); + if (feof($extf)) break; + if (false === $line) { + return false; + } + $first_lines .= $line; + } + return $first_lines; + } + + + function get_addon_header($header, &$source) { + if (preg_match('|'.$header.':(.*)$|mi', $source, $matches)) + return trim($matches[1]); + else + return false; + } + + function generic($dir, $output) { + $output = is_null($output)? "generic.pot" : $output; + return $this->xgettext('generic', $dir, $output, array()); + } + + function guess_plugin_slug($dir) { + if ('trunk' == basename($dir)) { + $slug = basename(dirname($dir)); + } elseif (in_array(basename(dirname($dir)), array('branches', 'tags'))) { + $slug = basename(dirname(dirname($dir))); + } else { + $slug = basename($dir); + } + return $slug; + } + + function wp_plugin($dir, $output, $slug = null) { + $placeholders = array(); + // guess plugin slug + if (is_null($slug)) { + $slug = $this->guess_plugin_slug($dir); + } + $main_file = $dir.'/'.$slug.'.php'; + if(!is_file($main_file)) $main_file = $dir.'/plugin.php'; + $source = $this->get_first_lines($main_file, $this->max_header_lines); + + $placeholders['version'] = $this->get_addon_header('Version', $source); + $placeholders['author'] = $this->get_addon_header('Author', $source); + $placeholders['name'] = $this->get_addon_header('Plugin Name', $source); + $placeholders['slug'] = $slug; + + $output = is_null($output)? "$slug.pot" : $output; + $res = $this->xgettext('wp-plugin', $dir, $output, $placeholders, array('(?:.+?/)?vendor/.*')); + if (!$res) return false; + $potextmeta = new PotExtMeta; + $res = $potextmeta->append($main_file, $output); + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell --no-wrap"); + return $res; + } + + function wp_theme($dir, $output, $slug = null) { + $placeholders = array(); + // guess plugin slug + if (is_null($slug)) { + $slug = $this->guess_plugin_slug($dir); + } + $main_file = $dir.'/style.css'; + $source = $this->get_first_lines($main_file, $this->max_header_lines); + + $placeholders['version'] = $this->get_addon_header('Version', $source); + $placeholders['author'] = $this->get_addon_header('Author', $source); + $placeholders['name'] = $this->get_addon_header('Theme Name', $source); + $placeholders['slug'] = $slug; + + $license = $this->get_addon_header( 'License', $source ); + if ( $license ) + $this->meta['wp-theme']['comments'] = "Copyright (C) {year} {author}\nThis file is distributed under the {$license}."; + else + $this->meta['wp-theme']['comments'] = "Copyright (C) {year} {author}\nThis file is distributed under the same license as the {package-name} package."; + + $output = is_null($output)? "$slug.pot" : $output; + $res = $this->xgettext('wp-theme', $dir, $output, $placeholders, array('(?:.+?/)?vendor/.*')); + if (! $res ) + return false; + $potextmeta = new PotExtMeta; + $res = $potextmeta->append( $main_file, $output, array( 'Theme Name', 'Theme URI', 'Description', 'Author', 'Author URI' ) ); + if ( ! $res ) + return false; + // If we're dealing with a pre-3.4 default theme, don't extract page templates before 3.4. + $extract_templates = ! in_array( $slug, array( 'twentyten', 'twentyeleven', 'default', 'classic' ) ); + if ( ! $extract_templates ) { + $wp_dir = dirname( dirname( dirname( $dir ) ) ); + $extract_templates = file_exists( "$wp_dir/wp-admin/user/about.php" ) || ! file_exists( "$wp_dir/wp-load.php" ); + } + if ( $extract_templates ) { + $res = $potextmeta->append( $dir, $output, array( 'Template Name' ) ); + if ( ! $res ) + return false; + $files = scandir( $dir ); + foreach ( $files as $file ) { + if ( '.' == $file[0] || 'CVS' == $file ) + continue; + if ( is_dir( $dir . '/' . $file ) ) { + $res = $potextmeta->append( $dir . '/' . $file, $output, array( 'Template Name' ) ); + if ( ! $res ) + return false; + } + } + } + /* Adding non-gettexted strings can repeat some phrases */ + $output_shell = escapeshellarg($output); + system("msguniq $output_shell -o $output_shell --no-wrap"); + return $res; + } + + function bp($dir, $output) { + $output = is_null($output)? "buddypress.pot" : $output; + return $this->xgettext('bp', $dir, $output, array(), array('bp-forums/bbpress/.*')); + } + + function glotpress( $dir, $output ) { + $output = is_null( $output ) ? "glotpress.pot" : $output; + return $this->xgettext( 'glotpress', $dir, $output ); + } + + function wporg_bb_forums( $dir, $output ) { + $output = is_null( $output ) ? 'wporg.pot' : $output; + return $this->xgettext( 'wporg-bb-forums', $dir, $output, array(), array( + 'bb-plugins/elfakismet/.*', + 'bb-plugins/support-forum/.*', + ) ); + } + + function rosetta( $dir, $output ) { + $output = is_null( $output )? 'rosetta.pot' : $output; + return $this->xgettext( 'rosetta', $dir, $output, array(), array(), array( + 'mu-plugins/rosetta.*\.php', + 'mu-plugins/rosetta/[^/]+\.php', + 'mu-plugins/rosetta/tmpl/.*\.php', + 'themes/rosetta/.*\.php', + ) ); + } + + function is_ms_file( $file_name ) { + $is_ms_file = false; + $prefix = substr( $file_name, 0, 2 ) === './'? '\./' : ''; + foreach( $this->ms_files as $ms_file ) + if ( preg_match( '|^'.$prefix.$ms_file.'$|', $file_name ) ) { + $is_ms_file = true; + break; + } + return $is_ms_file; + } + + function is_not_ms_file( $file_name ) { + return !$this->is_ms_file( $file_name ); + } +} + +// run the CLI only if the file +// wasn't included +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + $makepot = new MakePOT; + if ((3 == count($argv) || 4 == count($argv)) && in_array($method = str_replace('-', '_', $argv[1]), get_class_methods($makepot))) { + $res = call_user_func(array(&$makepot, $method), realpath($argv[2]), isset($argv[3])? $argv[3] : null); + if (false === $res) { + fwrite(STDERR, "Couldn't generate POT file!\n"); + } + } else { + $usage = "Usage: php makepot.php PROJECT DIRECTORY [OUTPUT]\n\n"; + $usage .= "Generate POT file from the files in DIRECTORY [OUTPUT]\n"; + $usage .= "Available projects: ".implode(', ', $makepot->projects)."\n"; + fwrite(STDERR, $usage); + exit(1); + } +} diff --git a/src/vendor/websharks/wp-i18n-tools/not-gettexted.php b/src/vendor/websharks/wp-i18n-tools/not-gettexted.php new file mode 100644 index 0000000..b6e730d --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/not-gettexted.php @@ -0,0 +1,234 @@ + 'command_extract', 'replace' => 'command_replace' ); + + + function logmsg() { + $args = func_get_args(); + if ($this->enable_logging) error_log(implode(' ', $args)); + } + + function stderr($msg, $nl=true) { + fwrite(STDERR, $msg.($nl? "\n" : "")); + } + + function cli_die($msg) { + $this->stderr($msg); + exit(1); + } + + function unchanged_token($token, $s='') { + return is_array($token)? $token[1] : $token; + } + + function ignore_token($token, $s='') { + return ''; + } + + function list_php_files($dir) { + $files = array(); + $items = scandir( $dir ); + foreach ( (array) $items as $item ) { + $full_item = $dir . '/' . $item; + if ('.' == $item || '..' == $item) + continue; + if ('.php' == substr($item, -4)) + $files[] = $full_item; + if (is_dir($full_item)) + $files += array_merge($files, NotGettexted::list_php_files($full_item, $files)); + } + return $files; + } + + + function make_string_aggregator($global_array_name, $filename) { + $a = $global_array_name; + return create_function('$string, $comment_id, $line_number', 'global $'.$a.'; $'.$a.'[] = array($string, $comment_id, '.var_export($filename, true).', $line_number);'); + } + + function make_mo_replacer($global_mo_name) { + $m = $global_mo_name; + return create_function('$token, $string', 'global $'.$m.'; return var_export($'.$m.'->translate($string), true);'); + } + + function walk_tokens(&$tokens, $string_action, $other_action, $register_action=null) { + + $current_comment_id = ''; + $current_string = ''; + $current_string_line = 0; + + $result = ''; + $line = 1; + + foreach($tokens as $token) { + if (is_array($token)) { + list($id, $text) = $token; + $line += substr_count($text, "\n"); + if ((T_ML_COMMENT == $id || T_COMMENT == $id) && preg_match('|/\*\s*(/?WP_I18N_[a-z_]+)\s*\*/|i', $text, $matches)) { + if ($this->STAGE_OUTSIDE == $stage) { + $stage = $this->STAGE_START_COMMENT; + $current_comment_id = $matches[1]; + $this->logmsg('start comment', $current_comment_id); + $result .= call_user_func($other_action, $token); + continue; + } + if ($this->STAGE_START_COMMENT <= $stage && $stage <= $this->STAGE_WHITESPACE_AFTER && '/'.$current_comment_id == $matches[1]) { + $stage = $this->STAGE_END_COMMENT; + $this->logmsg('end comment', $current_comment_id); + $result .= call_user_func($other_action, $token); + if (!is_null($register_action)) call_user_func($register_action, $current_string, $current_comment_id, $current_string_line); + continue; + } + } else if (T_CONSTANT_ENCAPSED_STRING == $id) { + if ($this->STAGE_START_COMMENT <= $stage && $stage < $this->STAGE_WHITESPACE_AFTER) { + eval('$current_string='.$text.';'); + $this->logmsg('string', $current_string); + $current_string_line = $line; + $result .= call_user_func($string_action, $token, $current_string); + continue; + } + } else if (T_WHITESPACE == $id) { + if ($this->STAGE_START_COMMENT <= $stage && $stage < $this->STAGE_STRING) { + $stage = $this->STAGE_WHITESPACE_BEFORE; + $this->logmsg('whitespace before'); + $result .= call_user_func($other_action, $token); + continue; + } + if ($this->STAGE_STRING < $stage && $stage < $this->STAGE_END_COMMENT) { + $stage = $this->STAGE_WHITESPACE_AFTER; + $this->logmsg('whitespace after'); + $result .= call_user_func($other_action, $token); + continue; + } + } + } + $result .= call_user_func($other_action, $token); + $stage = $this->STAGE_OUTSIDE; + $current_comment_id = ''; + $current_string = ''; + $current_string_line = 0; + } + return $result; + } + + + function command_extract() { + $args = func_get_args(); + $pot_filename = $args[0]; + if (isset($args[1]) && is_array($args[1])) + $filenames = $args[1]; + else + $filenames = array_slice($args, 1); + + $global_name = '__entries_'.mt_rand(1, 1000); + $GLOBALS[$global_name] = array(); + + foreach($filenames as $filename) { + $tokens = token_get_all(file_get_contents($filename)); + $aggregator = $this->make_string_aggregator($global_name, $filename); + $this->walk_tokens($tokens, array(&$this, 'ignore_token'), array(&$this, 'ignore_token'), $aggregator); + } + + $potf = '-' == $pot_filename? STDOUT : @fopen($pot_filename, 'a'); + if (false === $potf) { + $this->cli_die("Couldn't open pot file: $pot_filename"); + } + + foreach($GLOBALS[$global_name] as $item) { + @list($string, $comment_id, $filename, $line_number) = $item; + $filename = isset($filename)? preg_replace('|^\./|', '', $filename) : ''; + $ref_line_number = isset($line_number)? ":$line_number" : ''; + $args = array( + 'singular' => $string, + 'extracted_comments' => "Not gettexted string $comment_id", + 'references' => array("$filename$ref_line_number"), + ); + $entry = new Translation_Entry($args); + fwrite($potf, "\n".PO::export_entry($entry)."\n"); + } + if ('-' != $pot_filename) fclose($potf); + return true; + } + + function command_replace() { + $args = func_get_args(); + $mo_filename = $args[0]; + if (isset($args[1]) && is_array($args[1])) + $filenames = $args[1]; + else + $filenames = array_slice($args, 1); + + $global_name = '__mo_'.mt_rand(1, 1000); + $GLOBALS[$global_name] = new MO(); + $replacer = $this->make_mo_replacer($global_name); + + $res = $GLOBALS[$global_name]->import_from_file($mo_filename); + if (false === $res) { + $this->cli_die("Couldn't read MO file '$mo_filename'!"); + } + foreach($filenames as $filename) { + $source = file_get_contents($filename); + if ( strlen($source) > 150000 ) continue; + $tokens = token_get_all($source); + $new_file = $this->walk_tokens($tokens, $replacer, array(&$this, 'unchanged_token')); + $f = fopen($filename, 'w'); + fwrite($f, $new_file); + fclose($f); + } + return true; + } + + function usage() { + $this->stderr('php i18n-comments.php COMMAND OUTPUTFILE INPUTFILES'); + $this->stderr('Extracts and replaces strings, which cannot be gettexted'); + $this->stderr('Commands:'); + $this->stderr(' extract POTFILE PHPFILES appends the strings to POTFILE'); + $this->stderr(' replace MOFILE PHPFILES replaces strings in PHPFILES with translations from MOFILE'); + } + + function cli() { + global $argv, $commands; + if (count($argv) < 4 || !in_array($argv[1], array_keys($this->commands))) { + $this->usage(); + exit(1); + } + call_user_func_array(array(&$this, $this->commands[$argv[1]]), array_slice($argv, 2)); + } +} + +// run the CLI only if the file +// wasn't included +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + error_reporting(E_ALL); + $not_gettexted = new NotGettexted; + $not_gettexted->cli(); +} + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/entry.php b/src/vendor/websharks/wp-i18n-tools/pomo/entry.php new file mode 100644 index 0000000..097e92c --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/entry.php @@ -0,0 +1,78 @@ + $value) { + $this->$varname = $value; + } + if (isset($args['plural'])) $this->is_plural = true; + if (!is_array($this->translations)) $this->translations = array(); + if (!is_array($this->references)) $this->references = array(); + if (!is_array($this->flags)) $this->flags = array(); + } + + /** + * Generates a unique key for this entry + * + * @return string|bool the key or false if the entry is empty + */ + function key() { + if (is_null($this->singular)) return false; + // prepend context and EOT, like in MO files + return is_null($this->context)? $this->singular : $this->context.chr(4).$this->singular; + } + + function merge_with(&$other) { + $this->flags = array_unique( array_merge( $this->flags, $other->flags ) ); + $this->references = array_unique( array_merge( $this->references, $other->references ) ); + if ( $this->extracted_comments != $other->extracted_comments ) { + $this->extracted_comments .= $other->extracted_comments; + } + + } +} +endif; \ No newline at end of file diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/mo.php b/src/vendor/websharks/wp-i18n-tools/pomo/mo.php new file mode 100644 index 0000000..ff5c1b8 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/mo.php @@ -0,0 +1,258 @@ +is_resource()) + return false; + return $this->import_from_reader($reader); + } + + function export_to_file($filename) { + $fh = fopen($filename, 'wb'); + if ( !$fh ) return false; + $res = $this->export_to_file_handle( $fh ); + fclose($fh); + return $res; + } + + function export() { + $tmp_fh = fopen("php://temp", 'r+'); + if ( !$tmp_fh ) return false; + $this->export_to_file_handle( $tmp_fh ); + rewind( $tmp_fh ); + return stream_get_contents( $tmp_fh ); + } + + function is_entry_good_for_export( $entry ) { + if ( empty( $entry->translations ) ) { + return false; + } + + if ( !array_filter( $entry->translations ) ) { + return false; + } + + return true; + } + + function export_to_file_handle($fh) { + $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); + ksort($entries); + $magic = 0x950412de; + $revision = 0; + $total = count($entries) + 1; // all the headers are one entry + $originals_lenghts_addr = 28; + $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total; + $size_of_hash = 0; + $hash_addr = $translations_lenghts_addr + 8 * $total; + $current_addr = $hash_addr; + fwrite($fh, pack('V*', $magic, $revision, $total, $originals_lenghts_addr, + $translations_lenghts_addr, $size_of_hash, $hash_addr)); + fseek($fh, $originals_lenghts_addr); + + // headers' msgid is an empty string + fwrite($fh, pack('VV', 0, $current_addr)); + $current_addr++; + $originals_table = chr(0); + + foreach($entries as $entry) { + $originals_table .= $this->export_original($entry) . chr(0); + $length = strlen($this->export_original($entry)); + fwrite($fh, pack('VV', $length, $current_addr)); + $current_addr += $length + 1; // account for the NULL byte after + } + + $exported_headers = $this->export_headers(); + fwrite($fh, pack('VV', strlen($exported_headers), $current_addr)); + $current_addr += strlen($exported_headers) + 1; + $translations_table = $exported_headers . chr(0); + + foreach($entries as $entry) { + $translations_table .= $this->export_translations($entry) . chr(0); + $length = strlen($this->export_translations($entry)); + fwrite($fh, pack('VV', $length, $current_addr)); + $current_addr += $length + 1; + } + + fwrite($fh, $originals_table); + fwrite($fh, $translations_table); + return true; + } + + function export_original($entry) { + //TODO: warnings for control characters + $exported = $entry->singular; + if ($entry->is_plural) $exported .= chr(0).$entry->plural; + if (!is_null($entry->context)) $exported = $entry->context . chr(4) . $exported; + return $exported; + } + + function export_translations($entry) { + //TODO: warnings for control characters + return implode(chr(0), $entry->translations); + } + + function export_headers() { + $exported = ''; + foreach($this->headers as $header => $value) { + $exported.= "$header: $value\n"; + } + return $exported; + } + + function get_byteorder($magic) { + // The magic is 0x950412de + + // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 + $magic_little = (int) - 1794895138; + $magic_little_64 = (int) 2500072158; + // 0xde120495 + $magic_big = ((int) - 569244523) & 0xFFFFFFFF; + if ($magic_little == $magic || $magic_little_64 == $magic) { + return 'little'; + } else if ($magic_big == $magic) { + return 'big'; + } else { + return false; + } + } + + function import_from_reader($reader) { + $endian_string = MO::get_byteorder($reader->readint32()); + if (false === $endian_string) { + return false; + } + $reader->setEndian($endian_string); + + $endian = ('big' == $endian_string)? 'N' : 'V'; + + $header = $reader->read(24); + if ($reader->strlen($header) != 24) + return false; + + // parse header + $header = unpack("{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header); + if (!is_array($header)) + return false; + + extract( $header ); + + // support revision 0 of MO format specs, only + if ($revision != 0) + return false; + + // seek to data blocks + $reader->seekto($originals_lenghts_addr); + + // read originals' indices + $originals_lengths_length = $translations_lenghts_addr - $originals_lenghts_addr; + if ( $originals_lengths_length != $total * 8 ) + return false; + + $originals = $reader->read($originals_lengths_length); + if ( $reader->strlen( $originals ) != $originals_lengths_length ) + return false; + + // read translations' indices + $translations_lenghts_length = $hash_addr - $translations_lenghts_addr; + if ( $translations_lenghts_length != $total * 8 ) + return false; + + $translations = $reader->read($translations_lenghts_length); + if ( $reader->strlen( $translations ) != $translations_lenghts_length ) + return false; + + // transform raw data into set of indices + $originals = $reader->str_split( $originals, 8 ); + $translations = $reader->str_split( $translations, 8 ); + + // skip hash table + $strings_addr = $hash_addr + $hash_length * 4; + + $reader->seekto($strings_addr); + + $strings = $reader->read_all(); + $reader->close(); + + for ( $i = 0; $i < $total; $i++ ) { + $o = unpack( "{$endian}length/{$endian}pos", $originals[$i] ); + $t = unpack( "{$endian}length/{$endian}pos", $translations[$i] ); + if ( !$o || !$t ) return false; + + // adjust offset due to reading strings to separate space before + $o['pos'] -= $strings_addr; + $t['pos'] -= $strings_addr; + + $original = $reader->substr( $strings, $o['pos'], $o['length'] ); + $translation = $reader->substr( $strings, $t['pos'], $t['length'] ); + + if ('' === $original) { + $headers = $this->make_headers($translation); + $this->set_headers($headers); + } else { + $entry = &$this->make_entry($original, $translation); + $this->entries[$entry->key()] = &$entry; + } + } + return true; + } + + /** + * Build a Translation_Entry from original string and translation strings, + * found in a MO file + * + * @static + * @param string $original original string to translate from MO file. Might contain + * 0x04 as context separator or 0x00 as singular/plural separator + * @param string $translation translation string from MO file. Might contain + * 0x00 as a plural translations separator + */ + function &make_entry($original, $translation) { + $entry = new Translation_Entry(); + // look for context + $parts = explode(chr(4), $original); + if (isset($parts[1])) { + $original = $parts[1]; + $entry->context = $parts[0]; + } + // look for plural original + $parts = explode(chr(0), $original); + $entry->singular = $parts[0]; + if (isset($parts[1])) { + $entry->is_plural = true; + $entry->plural = $parts[1]; + } + // plural translations are also separated by \0 + $entry->translations = explode(chr(0), $translation); + return $entry; + } + + function select_plural_form($count) { + return $this->gettext_select_plural_form($count); + } + + function get_plural_forms_count() { + return $this->_nplurals; + } +} +endif; \ No newline at end of file diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/po.php b/src/vendor/websharks/wp-i18n-tools/pomo/po.php new file mode 100644 index 0000000..0ac5f4c --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/po.php @@ -0,0 +1,385 @@ +headers as $header => $value) { + $header_string.= "$header: $value\n"; + } + $poified = PO::poify($header_string); + if ($this->comments_before_headers) + $before_headers = $this->prepend_each_line(rtrim($this->comments_before_headers)."\n", '# '); + else + $before_headers = ''; + return rtrim("{$before_headers}msgid \"\"\nmsgstr $poified"); + } + + /** + * Exports all entries to PO format + * + * @return string sequence of mgsgid/msgstr PO strings, doesn't containt newline at the end + */ + function export_entries() { + //TODO sorting + return implode("\n\n", array_map(array('PO', 'export_entry'), $this->entries)); + } + + /** + * Exports the whole PO file as a string + * + * @param bool $include_headers whether to include the headers in the export + * @return string ready for inclusion in PO file string for headers and all the enrtries + */ + function export($include_headers = true) { + $res = ''; + if ($include_headers) { + $res .= $this->export_headers(); + $res .= "\n\n"; + } + $res .= $this->export_entries(); + return $res; + } + + /** + * Same as {@link export}, but writes the result to a file + * + * @param string $filename where to write the PO string + * @param bool $include_headers whether to include tje headers in the export + * @return bool true on success, false on error + */ + function export_to_file($filename, $include_headers = true) { + $fh = fopen($filename, 'w'); + if (false === $fh) return false; + $export = $this->export($include_headers); + $res = fwrite($fh, $export); + if (false === $res) return false; + return fclose($fh); + } + + /** + * Text to include as a comment before the start of the PO contents + * + * Doesn't need to include # in the beginning of lines, these are added automatically + */ + function set_comment_before_headers( $text ) { + $this->comments_before_headers = $text; + } + + /** + * Formats a string in PO-style + * + * @static + * @param string $string the string to format + * @return string the poified string + */ + public static function poify($string) { + $quote = '"'; + $slash = '\\'; + $newline = "\n"; + + $replaces = array( + "$slash" => "$slash$slash", + "$quote" => "$slash$quote", + "\t" => '\t', + ); + + $string = str_replace(array_keys($replaces), array_values($replaces), $string); + + $po = $quote.implode("${slash}n$quote$newline$quote", explode($newline, $string)).$quote; + // add empty string on first line for readbility + if (false !== strpos($string, $newline) && + (substr_count($string, $newline) > 1 || !($newline === substr($string, -strlen($newline))))) { + $po = "$quote$quote$newline$po"; + } + // remove empty strings + $po = str_replace("$newline$quote$quote", '', $po); + return $po; + } + + /** + * Gives back the original string from a PO-formatted string + * + * @static + * @param string $string PO-formatted string + * @return string enascaped string + */ + public static function unpoify($string) { + $escapes = array('t' => "\t", 'n' => "\n", '\\' => '\\'); + $lines = array_map('trim', explode("\n", $string)); + $lines = array_map(array('PO', 'trim_quotes'), $lines); + $unpoified = ''; + $previous_is_backslash = false; + foreach($lines as $line) { + preg_match_all('/./u', $line, $chars); + $chars = $chars[0]; + foreach($chars as $char) { + if (!$previous_is_backslash) { + if ('\\' == $char) + $previous_is_backslash = true; + else + $unpoified .= $char; + } else { + $previous_is_backslash = false; + $unpoified .= isset($escapes[$char])? $escapes[$char] : $char; + } + } + } + return $unpoified; + } + + /** + * Inserts $with in the beginning of every new line of $string and + * returns the modified string + * + * @static + * @param string $string prepend lines in this string + * @param string $with prepend lines with this string + */ + public static function prepend_each_line($string, $with) { + $php_with = var_export($with, true); + $lines = explode("\n", $string); + // do not prepend the string on the last empty line, artefact by explode + if ("\n" == substr($string, -1)) unset($lines[count($lines) - 1]); + $res = implode("\n", array_map(create_function('$x', "return $php_with.\$x;"), $lines)); + // give back the empty line, we ignored above + if ("\n" == substr($string, -1)) $res .= "\n"; + return $res; + } + + /** + * Prepare a text as a comment -- wraps the lines and prepends # + * and a special character to each line + * + * @access private + * @param string $text the comment text + * @param string $char character to denote a special PO comment, + * like :, default is a space + */ + public static function comment_block($text, $char=' ') { + $text = wordwrap($text, PO_MAX_LINE_LEN - 3); + return PO::prepend_each_line($text, "#$char "); + } + + /** + * Builds a string from the entry for inclusion in PO file + * + * @static + * @param object &$entry the entry to convert to po string + * @return string|bool PO-style formatted string for the entry or + * false if the entry is empty + */ + public static function export_entry(&$entry) { + if (is_null($entry->singular)) return false; + $po = array(); + if (!empty($entry->translator_comments)) $po[] = PO::comment_block($entry->translator_comments); + if (!empty($entry->extracted_comments)) $po[] = PO::comment_block($entry->extracted_comments, '.'); + if (!empty($entry->references)) $po[] = PO::comment_block(implode(' ', $entry->references), ':'); + if (!empty($entry->flags)) $po[] = PO::comment_block(implode(", ", $entry->flags), ','); + if (!is_null($entry->context)) $po[] = 'msgctxt '.PO::poify($entry->context); + $po[] = 'msgid '.PO::poify($entry->singular); + if (!$entry->is_plural) { + $translation = empty($entry->translations)? '' : $entry->translations[0]; + $po[] = 'msgstr '.PO::poify($translation); + } else { + $po[] = 'msgid_plural '.PO::poify($entry->plural); + $translations = empty($entry->translations)? array('', '') : $entry->translations; + foreach($translations as $i => $translation) { + $po[] = "msgstr[$i] ".PO::poify($translation); + } + } + return implode("\n", $po); + } + + function import_from_file($filename) { + $f = fopen($filename, 'r'); + if (!$f) return false; + $lineno = 0; + while (true) { + $res = $this->read_entry($f, $lineno); + if (!$res) break; + if ($res['entry']->singular == '') { + $headers = $this->make_headers($res['entry']->translations[0]); + $this->set_headers($headers); + } else { + $this->add_entry($res['entry']); + } + } + PO::read_line($f, 'clear'); + if ( false === $res ) { + return false; + } + if ( ! $this->headers && ! $this->entries ) { + return false; + } + return true; + } + + function read_entry($f, $lineno = 0) { + $entry = new Translation_Entry(); + // where were we in the last step + // can be: comment, msgctxt, msgid, msgid_plural, msgstr, msgstr_plural + $context = ''; + $msgstr_index = 0; + $is_final = create_function('$context', 'return $context == "msgstr" || $context == "msgstr_plural";'); + while (true) { + $lineno++; + $line = PO::read_line($f); + if (!$line) { + if (feof($f)) { + if ($is_final($context)) + break; + elseif (!$context) // we haven't read a line and eof came + return null; + else + return false; + } else { + return false; + } + } + if ($line == "\n") continue; + $line = trim($line); + if (preg_match('/^#/', $line, $m)) { + // the comment is the start of a new entry + if ($is_final($context)) { + PO::read_line($f, 'put-back'); + $lineno--; + break; + } + // comments have to be at the beginning + if ($context && $context != 'comment') { + return false; + } + // add comment + $this->add_comment_to_entry($entry, $line); + } elseif (preg_match('/^msgctxt\s+(".*")/', $line, $m)) { + if ($is_final($context)) { + PO::read_line($f, 'put-back'); + $lineno--; + break; + } + if ($context && $context != 'comment') { + return false; + } + $context = 'msgctxt'; + $entry->context .= PO::unpoify($m[1]); + } elseif (preg_match('/^msgid\s+(".*")/', $line, $m)) { + if ($is_final($context)) { + PO::read_line($f, 'put-back'); + $lineno--; + break; + } + if ($context && $context != 'msgctxt' && $context != 'comment') { + return false; + } + $context = 'msgid'; + $entry->singular .= PO::unpoify($m[1]); + } elseif (preg_match('/^msgid_plural\s+(".*")/', $line, $m)) { + if ($context != 'msgid') { + return false; + } + $context = 'msgid_plural'; + $entry->is_plural = true; + $entry->plural .= PO::unpoify($m[1]); + } elseif (preg_match('/^msgstr\s+(".*")/', $line, $m)) { + if ($context != 'msgid') { + return false; + } + $context = 'msgstr'; + $entry->translations = array(PO::unpoify($m[1])); + } elseif (preg_match('/^msgstr\[(\d+)\]\s+(".*")/', $line, $m)) { + if ($context != 'msgid_plural' && $context != 'msgstr_plural') { + return false; + } + $context = 'msgstr_plural'; + $msgstr_index = $m[1]; + $entry->translations[$m[1]] = PO::unpoify($m[2]); + } elseif (preg_match('/^".*"$/', $line)) { + $unpoified = PO::unpoify($line); + switch ($context) { + case 'msgid': + $entry->singular .= $unpoified; break; + case 'msgctxt': + $entry->context .= $unpoified; break; + case 'msgid_plural': + $entry->plural .= $unpoified; break; + case 'msgstr': + $entry->translations[0] .= $unpoified; break; + case 'msgstr_plural': + $entry->translations[$msgstr_index] .= $unpoified; break; + default: + return false; + } + } else { + return false; + } + } + if (array() == array_filter($entry->translations, create_function('$t', 'return $t || "0" === $t;'))) { + $entry->translations = array(); + } + return array('entry' => $entry, 'lineno' => $lineno); + } + + function read_line($f, $action = 'read') { + static $last_line = ''; + static $use_last_line = false; + if ('clear' == $action) { + $last_line = ''; + return true; + } + if ('put-back' == $action) { + $use_last_line = true; + return true; + } + $line = $use_last_line? $last_line : fgets($f); + $line = ( "\r\n" == substr( $line, -2 ) ) ? rtrim( $line, "\r\n" ) . "\n" : $line; + $last_line = $line; + $use_last_line = false; + return $line; + } + + function add_comment_to_entry(&$entry, $po_comment_line) { + $first_two = substr($po_comment_line, 0, 2); + $comment = trim(substr($po_comment_line, 2)); + if ('#:' == $first_two) { + $entry->references = array_merge($entry->references, preg_split('/\s+/', $comment)); + } elseif ('#.' == $first_two) { + $entry->extracted_comments = trim($entry->extracted_comments . "\n" . $comment); + } elseif ('#,' == $first_two) { + $entry->flags = array_merge($entry->flags, preg_split('/,\s*/', $comment)); + } else { + $entry->translator_comments = trim($entry->translator_comments . "\n" . $comment); + } + } + + public static function trim_quotes($s) { + if ( substr($s, 0, 1) == '"') $s = substr($s, 1); + if ( substr($s, -1, 1) == '"') $s = substr($s, 0, -1); + return $s; + } +} +endif; diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/app.php b/src/vendor/websharks/wp-i18n-tools/pomo/sample/app.php new file mode 100644 index 0000000..e0a0bf7 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/sample/app.php @@ -0,0 +1,63 @@ +translate($text); +} + +function _e($text, $domain = 'default') { + $translations = &get_translations($domain); + echo $translations->translate($text); +} + +function __n($singular, $plural, $count, $domain = 'default') { + $translations = &get_translations($domain); + return $translations->translate_plural($singular, $plural, $count); +} + +function &load_translations($mo_filename) { + if (is_readable($mo_filename)) { + $translations = new MO(); + $translations->import_from_file($mo_filename); + } else { + $translations = new Translations(); + } + return $translations; +} + +// get the locale from somewhere: subomain, config, GET var, etc. +// it can be safely empty +$locale = 'bg'; +$translations = array(); +$empty_translations = & new Translations(); + +function load_textdomain($domain, $mofile) { + global $translations; + $translations[$domain] = &load_translations($mofile); +} + +function &get_translations($domain) { + global $translations, $empty_translations; + return isset($translations[$domain])? $translations[$domain] : $empty_translations; +} + +// load the translations +load_textdomain('default', "languages/$locale.mo"); +load_textdomain('side', "languages/$locale-side.mo"); + +//here comes the real app +$user = 'apok'; +$messages = rand(0, 2); + +printf(__('Welcome %s!')."\n", $user); + +printf(__n('You have one new message.', 'You have %s new messages.', $messages)."\n", $messages); + +echo __("A string with low priority!", 'side')."\n"; + +_e("Bye\n"); +?> diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app-side.pot b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app-side.pot new file mode 100644 index 0000000..19998d7 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app-side.pot @@ -0,0 +1,21 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n" +"POT-Creation-Date: 2008-06-06 23:10+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +msgid "A string with low priority!" +msgstr "" diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app.pot b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app.pot new file mode 100644 index 0000000..51d1fa1 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/app.pot @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n" +"POT-Creation-Date: 2008-06-06 23:10+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: app.php:46 +#, php-format +msgid "Welcome %s!" +msgstr "" + +#: app.php:48 +#, php-format +msgid "You have one new message." +msgid_plural "You have %s new messages." +msgstr[0] "" +msgstr[1] "" + +#: app.php:50 +msgid "Bye\n" +msgstr "" diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.mo b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.mo new file mode 100644 index 0000000..0cb44df Binary files /dev/null and b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.mo differ diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.po b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.po new file mode 100644 index 0000000..a02bd25 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg-side.po @@ -0,0 +1,19 @@ +# Bulgarian translations for PACKAGE package. +# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Nikolay Bachiyski , 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n" +"POT-Creation-Date: 2008-06-06 23:10+0300\n" +"PO-Revision-Date: 2008-06-06 22:54+0300\n" +"Last-Translator: Nikolay Bachiyski \n" +"Language-Team: Bulgarian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "A string with low priority!" +msgstr "Смотан низ!" diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.mo b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.mo new file mode 100644 index 0000000..97abf08 Binary files /dev/null and b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.mo differ diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.po b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.po new file mode 100644 index 0000000..e0c3ce6 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/sample/languages/bg.po @@ -0,0 +1,32 @@ +# Bulgarian translations for PACKAGE package. +# Copyright (C) 2008 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Nikolay Bachiyski , 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: wp-polyglots@lists.automattic.com\n" +"POT-Creation-Date: 2008-06-06 23:10+0300\n" +"PO-Revision-Date: 2008-06-06 22:54+0300\n" +"Last-Translator: Nikolay Bachiyski \n" +"Language-Team: Bulgarian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: app.php:46 +#, php-format +msgid "Welcome %s!" +msgstr "Добре дошъл, %s!" + +#: app.php:48 +#, php-format +msgid "You have one new message." +msgid_plural "You have %s new messages." +msgstr[0] "Имате едно ново съобщение." +msgstr[1] "Имате %s нови съобщения." + +#: app.php:50 +msgid "Bye\n" +msgstr "Чао\n" diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/streams.php b/src/vendor/websharks/wp-i18n-tools/pomo/streams.php new file mode 100644 index 0000000..dbb1de8 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/streams.php @@ -0,0 +1,209 @@ + + * + * @version $Id: streams.php 718 2012-10-31 00:32:02Z nbachiyski $ + * @package pomo + * @subpackage streams + */ + +if ( !class_exists( 'POMO_Reader' ) ): +class POMO_Reader { + + var $endian = 'little'; + var $_post = ''; + + function POMO_Reader() { + $this->is_overloaded = ((ini_get("mbstring.func_overload") & 2) != 0) && function_exists('mb_substr'); + $this->_pos = 0; + } + + /** + * Sets the endianness of the file. + * + * @param $endian string 'big' or 'little' + */ + function setEndian($endian) { + $this->endian = $endian; + } + + /** + * Reads a 32bit Integer from the Stream + * + * @return mixed The integer, corresponding to the next 32 bits from + * the stream of false if there are not enough bytes or on error + */ + function readint32() { + $bytes = $this->read(4); + if (4 != $this->strlen($bytes)) + return false; + $endian_letter = ('big' == $this->endian)? 'N' : 'V'; + $int = unpack($endian_letter, $bytes); + return array_shift($int); + } + + /** + * Reads an array of 32-bit Integers from the Stream + * + * @param integer count How many elements should be read + * @return mixed Array of integers or false if there isn't + * enough data or on error + */ + function readint32array($count) { + $bytes = $this->read(4 * $count); + if (4*$count != $this->strlen($bytes)) + return false; + $endian_letter = ('big' == $this->endian)? 'N' : 'V'; + return unpack($endian_letter.$count, $bytes); + } + + + function substr($string, $start, $length) { + if ($this->is_overloaded) { + return mb_substr($string, $start, $length, 'ascii'); + } else { + return substr($string, $start, $length); + } + } + + function strlen($string) { + if ($this->is_overloaded) { + return mb_strlen($string, 'ascii'); + } else { + return strlen($string); + } + } + + function str_split($string, $chunk_size) { + if (!function_exists('str_split')) { + $length = $this->strlen($string); + $out = array(); + for ($i = 0; $i < $length; $i += $chunk_size) + $out[] = $this->substr($string, $i, $chunk_size); + return $out; + } else { + return str_split( $string, $chunk_size ); + } + } + + + function pos() { + return $this->_pos; + } + + function is_resource() { + return true; + } + + function close() { + return true; + } +} +endif; + +if ( !class_exists( 'POMO_FileReader' ) ): +class POMO_FileReader extends POMO_Reader { + function POMO_FileReader($filename) { + parent::POMO_Reader(); + $this->_f = fopen($filename, 'rb'); + } + + function read($bytes) { + return fread($this->_f, $bytes); + } + + function seekto($pos) { + if ( -1 == fseek($this->_f, $pos, SEEK_SET)) { + return false; + } + $this->_pos = $pos; + return true; + } + + function is_resource() { + return is_resource($this->_f); + } + + function feof() { + return feof($this->_f); + } + + function close() { + return fclose($this->_f); + } + + function read_all() { + $all = ''; + while ( !$this->feof() ) + $all .= $this->read(4096); + return $all; + } +} +endif; + +if ( !class_exists( 'POMO_StringReader' ) ): +/** + * Provides file-like methods for manipulating a string instead + * of a physical file. + */ +class POMO_StringReader extends POMO_Reader { + + var $_str = ''; + + function POMO_StringReader($str = '') { + parent::POMO_Reader(); + $this->_str = $str; + $this->_pos = 0; + } + + + function read($bytes) { + $data = $this->substr($this->_str, $this->_pos, $bytes); + $this->_pos += $bytes; + if ($this->strlen($this->_str) < $this->_pos) $this->_pos = $this->strlen($this->_str); + return $data; + } + + function seekto($pos) { + $this->_pos = $pos; + if ($this->strlen($this->_str) < $this->_pos) $this->_pos = $this->strlen($this->_str); + return $this->_pos; + } + + function length() { + return $this->strlen($this->_str); + } + + function read_all() { + return $this->substr($this->_str, $this->_pos, $this->strlen($this->_str)); + } + +} +endif; + +if ( !class_exists( 'POMO_CachedFileReader' ) ): +/** + * Reads the contents of the file in the beginning. + */ +class POMO_CachedFileReader extends POMO_StringReader { + function POMO_CachedFileReader($filename) { + parent::POMO_StringReader(); + $this->_str = file_get_contents($filename); + if (false === $this->_str) + return false; + $this->_pos = 0; + } +} +endif; + +if ( !class_exists( 'POMO_CachedIntFileReader' ) ): +/** + * Reads the contents of the file in the beginning. + */ +class POMO_CachedIntFileReader extends POMO_CachedFileReader { + function POMO_CachedIntFileReader($filename) { + parent::POMO_CachedFileReader($filename); + } +} +endif; \ No newline at end of file diff --git a/src/vendor/websharks/wp-i18n-tools/pomo/translations.php b/src/vendor/websharks/wp-i18n-tools/pomo/translations.php new file mode 100644 index 0000000..b5f59c0 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pomo/translations.php @@ -0,0 +1,275 @@ +key(); + if (false === $key) return false; + $this->entries[$key] = &$entry; + return true; + } + + function add_entry_or_merge($entry) { + if (is_array($entry)) { + $entry = new Translation_Entry($entry); + } + $key = $entry->key(); + if (false === $key) return false; + if (isset($this->entries[$key])) + $this->entries[$key]->merge_with($entry); + else + $this->entries[$key] = &$entry; + return true; + } + + /** + * Sets $header PO header to $value + * + * If the header already exists, it will be overwritten + * + * TODO: this should be out of this class, it is gettext specific + * + * @param string $header header name, without trailing : + * @param string $value header value, without trailing \n + */ + function set_header($header, $value) { + $this->headers[$header] = $value; + } + + function set_headers($headers) { + foreach($headers as $header => $value) { + $this->set_header($header, $value); + } + } + + function get_header($header) { + return isset($this->headers[$header])? $this->headers[$header] : false; + } + + function translate_entry(&$entry) { + $key = $entry->key(); + return isset($this->entries[$key])? $this->entries[$key] : false; + } + + function translate($singular, $context=null) { + $entry = new Translation_Entry(array('singular' => $singular, 'context' => $context)); + $translated = $this->translate_entry($entry); + return ($translated && !empty($translated->translations))? $translated->translations[0] : $singular; + } + + /** + * Given the number of items, returns the 0-based index of the plural form to use + * + * Here, in the base Translations class, the common logic for English is implemented: + * 0 if there is one element, 1 otherwise + * + * This function should be overrided by the sub-classes. For example MO/PO can derive the logic + * from their headers. + * + * @param integer $count number of items + */ + function select_plural_form($count) { + return 1 == $count? 0 : 1; + } + + function get_plural_forms_count() { + return 2; + } + + function translate_plural($singular, $plural, $count, $context = null) { + $entry = new Translation_Entry(array('singular' => $singular, 'plural' => $plural, 'context' => $context)); + $translated = $this->translate_entry($entry); + $index = $this->select_plural_form($count); + $total_plural_forms = $this->get_plural_forms_count(); + if ($translated && 0 <= $index && $index < $total_plural_forms && + is_array($translated->translations) && + isset($translated->translations[$index])) + return $translated->translations[$index]; + else + return 1 == $count? $singular : $plural; + } + + /** + * Merge $other in the current object. + * + * @param Object &$other Another Translation object, whose translations will be merged in this one + * @return void + **/ + function merge_with(&$other) { + foreach( $other->entries as $entry ) { + $this->entries[$entry->key()] = $entry; + } + } + + function merge_originals_with(&$other) { + foreach( $other->entries as $entry ) { + if ( !isset( $this->entries[$entry->key()] ) ) + $this->entries[$entry->key()] = $entry; + else + $this->entries[$entry->key()]->merge_with($entry); + } + } +} + +class Gettext_Translations extends Translations { + /** + * The gettext implementation of select_plural_form. + * + * It lives in this class, because there are more than one descendand, which will use it and + * they can't share it effectively. + * + */ + function gettext_select_plural_form($count) { + if (!isset($this->_gettext_select_plural_form) || is_null($this->_gettext_select_plural_form)) { + list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms')); + $this->_nplurals = $nplurals; + $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression); + } + return call_user_func($this->_gettext_select_plural_form, $count); + } + + function nplurals_and_expression_from_header($header) { + if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches)) { + $nplurals = (int)$matches[1]; + $expression = trim($this->parenthesize_plural_exression($matches[2])); + return array($nplurals, $expression); + } else { + return array(2, 'n != 1'); + } + } + + /** + * Makes a function, which will return the right translation index, according to the + * plural forms header + */ + function make_plural_form_function($nplurals, $expression) { + $expression = str_replace('n', '$n', $expression); + $func_body = " + \$index = (int)($expression); + return (\$index < $nplurals)? \$index : $nplurals - 1;"; + return create_function('$n', $func_body); + } + + /** + * Adds parantheses to the inner parts of ternary operators in + * plural expressions, because PHP evaluates ternary oerators from left to right + * + * @param string $expression the expression without parentheses + * @return string the expression with parentheses added + */ + function parenthesize_plural_exression($expression) { + $expression .= ';'; + $res = ''; + $depth = 0; + for ($i = 0; $i < strlen($expression); ++$i) { + $char = $expression[$i]; + switch ($char) { + case '?': + $res .= ' ? ('; + $depth++; + break; + case ':': + $res .= ') : ('; + break; + case ';': + $res .= str_repeat(')', $depth) . ';'; + $depth= 0; + break; + default: + $res .= $char; + } + } + return rtrim($res, ';'); + } + + function make_headers($translation) { + $headers = array(); + // sometimes \ns are used instead of real new lines + $translation = str_replace('\n', "\n", $translation); + $lines = explode("\n", $translation); + foreach($lines as $line) { + $parts = explode(':', $line, 2); + if (!isset($parts[1])) continue; + $headers[trim($parts[0])] = trim($parts[1]); + } + return $headers; + } + + function set_header($header, $value) { + parent::set_header($header, $value); + if ('Plural-Forms' == $header) { + list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header($this->get_header('Plural-Forms')); + $this->_nplurals = $nplurals; + $this->_gettext_select_plural_form = $this->make_plural_form_function($nplurals, $expression); + } + } +} +endif; + +if ( !class_exists( 'NOOP_Translations' ) ): +/** + * Provides the same interface as Translations, but doesn't do anything + */ +class NOOP_Translations { + var $entries = array(); + var $headers = array(); + + function add_entry($entry) { + return true; + } + + function set_header($header, $value) { + } + + function set_headers($headers) { + } + + function get_header($header) { + return false; + } + + function translate_entry(&$entry) { + return false; + } + + function translate($singular, $context=null) { + return $singular; + } + + function select_plural_form($count) { + return 1 == $count? 0 : 1; + } + + function get_plural_forms_count() { + return 2; + } + + function translate_plural($singular, $plural, $count, $context = null) { + return 1 == $count? $singular : $plural; + } + + function merge_with(&$other) { + } +} +endif; diff --git a/src/vendor/websharks/wp-i18n-tools/pot-ext-meta.php b/src/vendor/websharks/wp-i18n-tools/pot-ext-meta.php new file mode 100644 index 0000000..0e451ec --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/pot-ext-meta.php @@ -0,0 +1,76 @@ +headers as $header) { + $string = MakePOT::get_addon_header($header, $source); + if (!$string) continue; + $args = array( + 'singular' => $string, + 'extracted_comments' => $header.' of the plugin/theme', + ); + $entry = new Translation_Entry($args); + $pot .= "\n".PO::export_entry($entry)."\n"; + } + return $pot; + } + + function append( $ext_filename, $pot_filename, $headers = null ) { + if ( $headers ) + $this->headers = (array) $headers; + if ( is_dir( $ext_filename ) ) { + $pot = implode('', array_map(array(&$this, 'load_from_file'), glob("$ext_filename/*.php"))); + } else { + $pot = $this->load_from_file($ext_filename); + } + $potf = '-' == $pot_filename? STDOUT : fopen($pot_filename, 'a'); + if (!$potf) return false; + fwrite($potf, $pot); + if ('-' != $pot_filename) fclose($potf); + return true; + } +} + +$included_files = get_included_files(); +if ($included_files[0] == __FILE__) { + ini_set('display_errors', 1); + $potextmeta = new PotExtMeta; + if (!isset($argv[1])) { + $potextmeta->usage(); + } + $potextmeta->append( $argv[1], isset( $argv[2] ) ? $argv[2] : '-', isset( $argv[3] ) ? $argv[3] : null ); +} + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/setup_locale b/src/vendor/websharks/wp-i18n-tools/setup_locale new file mode 100644 index 0000000..be645e8 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/setup_locale @@ -0,0 +1,40 @@ +#!/bin/sh +# setup_locale - Creates the directory structure for a new locale in the +# wordpress-i18n repository for a new locale. + +# First argument is the locale. Ex: es_ES +locale=$1 + +if [ -z "$locale" ] +then + echo "A locale must be specified." + echo Usage is: $0 locale [repository] + exit +fi + +# Second argument is the base directory of your working copy +# of wordpress-i18n. +base=$2 + +if [ -z "$base" ] +then + base=. +fi + +if [ ! -d "$base" ] +then + echo "Directory $base does not exist." + echo Usage is: $0 locale [repository] + exit +fi + +cd $base + +svn mkdir $locale +svn mkdir $locale/trunk +svn mkdir $locale/branches +svn mkdir $locale/tags + +svn mkdir $locale/trunk/dist + +svn commit $locale -m "$locale" --force-log diff --git a/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-result.php b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-result.php new file mode 100644 index 0000000..c233bfc --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-result.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn\'t work for all server setups. The safest way is to manually create the file.

Create a Configuration File' /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ 'Translation: WordPress › Error' /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-work.php b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-work.php new file mode 100644 index 0000000..c233bfc --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0-work.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn\'t work for all server setups. The safest way is to manually create the file.

Create a Configuration File' /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ 'Translation: WordPress › Error' /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.mo b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.mo new file mode 100644 index 0000000..4042fb0 Binary files /dev/null and b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.mo differ diff --git a/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.php b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.php new file mode 100644 index 0000000..4faaccd --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.php @@ -0,0 +1,24 @@ +wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" /*/WP_I18N_CONFIG*/, $path.'setup-config.php'), /*WP_I18N_ERROR*/ "WordPress › Error" /*/WP_I18N_ERROR*/); +} + +$wp_did_header = true; + +require_once( dirname(__FILE__) . '/wp-config.php'); + +wp(); + +require_once(ABSPATH . WPINC . '/template-loader.php'); + +endif; + +?> diff --git a/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.po b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.po new file mode 100644 index 0000000..606ac7e --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/data/not-gettexted-0.po @@ -0,0 +1,21 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2008-04-22 19:23+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Not gettexted string WP_I18N_CONFIG +#: not-gettext-0.php:11 +msgid " There doesn't seem to be a wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" +msgstr "Translation: There doesn't seem to be a wp-config.php file. I need this before we can get started. Need more help? We got it. You can create a wp-config.php file through a web interface, but this doesn't work for all server setups. The safest way is to manually create the file.

Create a Configuration File" + +#. Not gettexted string WP_I18N_ERROR +#: not-gettext-0.php:11 +msgid "WordPress › Error" +msgstr "Translation: WordPress › Error" + diff --git a/src/vendor/websharks/wp-i18n-tools/t/run.sh b/src/vendor/websharks/wp-i18n-tools/t/run.sh new file mode 100644 index 0000000..bf1ac0d --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +phpunit Test_Not_Gettexted test_not-gettexted.php diff --git a/src/vendor/websharks/wp-i18n-tools/t/test_not-gettexted.php b/src/vendor/websharks/wp-i18n-tools/t/test_not-gettexted.php new file mode 100644 index 0000000..511b040 --- /dev/null +++ b/src/vendor/websharks/wp-i18n-tools/t/test_not-gettexted.php @@ -0,0 +1,48 @@ +ng = new NotGettexted; + } + + function test_make_string_aggregator() { + global $baba; + $f = $this->ng->make_string_aggregator( 'baba', 'baba.php' ); + call_user_func( $f, 'x', 'y', 'z' ); + call_user_func( $f, 'a', 'b', 'c' ); + $this->assertEquals( array( array( 'x', 'y', 'baba.php', 'z'), array( 'a', 'b', 'baba.php', 'c' ) ), $baba ); + } + + function test_walk() { + $code = ' +'; + $tokens = token_get_all($code); + $this->assertEquals( '', $this->ng->walk_tokens( $tokens, array(&$this->ng, 'ignore_token'), array(&$this->ng, 'ignore_token') ) ); + $this->assertEquals( '"yes"\'We died %d times!\'', $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'ignore_token') ) ); + $this->assertEquals( $code, $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'unchanged_token') ) ); + $this->assertEquals( $code, $this->ng->walk_tokens( $tokens, array(&$this->ng, 'unchanged_token'), array(&$this->ng, 'unchanged_token') ) ); + } + + function test_replace() { + # copy to a new file, so that we don't corrupt the old one + copy( 'data/not-gettexted-0.php', 'data/not-gettexted-0-work.php' ); + $this->ng->command_replace( 'data/not-gettexted-0.mo', 'data/not-gettexted-0-work.php' ); + $this->assertEquals( file_get_contents( 'data/not-gettexted-0-result.php' ), file_get_contents( 'data/not-gettexted-0-work.php' ) ); + unlink( 'data/not-gettexted-0-work.php' ); + } +} \ No newline at end of file diff --git a/uninstall.php b/uninstall.php index 703bf49..1e4ab23 100644 --- a/uninstall.php +++ b/uninstall.php @@ -3,6 +3,6 @@ exit('Do NOT access this file directly: '.basename(__FILE__)); } $GLOBALS['wp_php_rv'] = '5.4'; //php-required-version// -if (require(dirname(__FILE__).'/src/vendor/websharks/wp-php-rv/src/includes/check.php')) { - require_once dirname(__FILE__).'/src/includes/uninstall.php'; +if (require(__DIR__.'/src/vendor/websharks/wp-php-rv/src/includes/check.php')) { + require_once __DIR__.'/src/includes/uninstall.php'; }