diff --git a/.gitchange b/.gitchange index 34e1f6d..cc2d6f7 100644 --- a/.gitchange +++ b/.gitchange @@ -1,4 +1,4 @@ 1460135613 49dd4154d8f5d3b70691c8e3f52a063a49137fb7:57be33556df067.43016776 -853d9120b42cf4f2eabe2f35ea18a6ace7170ea6:5824430b1d7e09.47123954 -d0e05c36184f81b133761b71ca6ee0136be67409:5830d0b4af5435.34387109 +853d9120b42cf4f2eabe2f35ea18a6ace7170ea6:5849aaa17584a2.95309978 +d0e05c36184f81b133761b71ca6ee0136be67409:58509b56682b37.29646922 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0132a1e..09146bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,28 @@ -= v161119 = += [coming soon] = + +- **New Pro Feature (Mobile-Adaptive Mode):** This release adds a new feature that is designed to improve compatibility with Adaptive themes for mobile devices. To learn more, please see: **Dashboard → Comet Cache Pro → Plugin Options → Mobile-Adaptive Mode**. See also: [Issue #471](https://github.com/websharks/comet-cache/issues/471) and the screenshots [here](https://github.com/websharks/comet-cache-pro/pull/303#issuecomment-266230409). + +- **Static CDN Filters (`srcset`):** This release enhances Static CDN Filters in Comet Cache Pro. Static CDN Filters are now smart enough to filter all image sources included in an `srcset=""` attribute that is generated by WordPress. See [Issue #660](https://github.com/websharks/comet-cache/issues/660). If you'd like to learn more about `srcset=""`, see [this article at WordPress.org](https://make.wordpress.org/core/2015/11/10/responsive-images-in-wordpress-4-4/). + +- **Compatibility:** Avoid deprecated `wp_get_sites()` and use `get_sites()` instead. See [Issue #848](https://github.com/websharks/comet-cache/issues/848). + +- **Auto-Clearing:** This release makes Comet Cache smart enough to detect when a user is deleted (or removed from a child blog in a Network). At which time the Author page for that user will be cleared from the cache so it can be regenerated automatically. See [Issue #304](https://github.com/websharks/comet-cache/issues/304). + +- **Watered-Down Regex Documentation:** Adding notes to the inline documentation (in the software) about the use of `^` and `$` in some places where these special characters are not fully supported. Also adding the same notes to the [Watered-Down Regex KB Article](https://cometcache.com/kb-article/watered-down-regex-syntax/). See also: [Issue #611](https://github.com/websharks/comet-cache/issues/611). + +- **Multibyte Compatibility:** This release improves support for WordPress Permalinks that contain UTF-8 symbols (or emojis) in them. More specifically, this release adds the `/u` flag to all `preg_*()` calls in cache clearing routines that generate cache paths from Watered-Down Regex patterns entered by a site owner. See: [Issue #611](https://github.com/websharks/comet-cache/issues/611). + +- **Widget Change Detection:** Comet Cache can now detect when **Appearance → Widgets** are added/edited/removed, and Comet Cache will automatically clear the cache so that your site remains up-to-date. See [Issue #411](https://github.com/websharks/comet-cache/issues/411). + +- **Automatic Background Updates:** It is now possible (in Comet Cache Pro) to enable automatic background updates that occur quietly in the background whenever new features, bug fixes, or security issues are addressed by our developers. See: **Dashboard → Comet Cache Pro → Config. Options → Update Credentials**. See also: [Issue #827](https://github.com/websharks/comet-cache/issues/827). + +- **HTML Compressor:** Updated to the latest available release of the HTML Compressor (v161208) with improved support for [Accelerated Mobile Pages](https://www.ampproject.org/). See: [Issue #733](https://github.com/websharks/comet-cache/issues/733). See also: [HTML Compressor v161208 changelog](https://github.com/websharks/html-compressor/releases/tag/161208). + +- **HTML Compressor / AMP Compatibility:** Improved compatibility with [Accelerated Mobile Pages](https://www.ampproject.org/). There is a new HTML Compressor option that is enabled by default and it makes Comet Cache smart enough to auto-detect and selectively disable portions of the HTML Compressor that are incompatible with the AMP spec; i.e., routines that are not necessary when serving APMd pages. In short, if the URI being compressed ends with `/amp/`, or the document contains a top-level `` tag (`` is accepted as well), then features which are incompatible with [Accelerated Mobile Pages](https://www.ampproject.org/) will be disabled accordingly. + +- **Bug Fix:** Improving OPcache detection. Now considering INI option `opcache.restrict_api`. Comet Cache is now smart enough to avoid the error: _PHP Warning: Zend OPcache API is restricted by "restrict_api" configuration directive_. See [Issue #733](https://github.com/websharks/comet-cache/issues/733). + += v161119 = - **Bug Fix:** Avoid browser autocomplete in configuration fields by adding `autocomplete="off"` to all form tags in Comet Cache menu pages. See [Issue #832](https://github.com/websharks/comet-cache/issues/832). - **Bug Fix:** Fixed a broken link to the [Static CDN Filters tutorial for MaxCDN integration](http://cometcache.com/r/static-cdn-filters-maxcdn/). Props @kristineds. See [Issue #842](https://github.com/websharks/comet-cache/issues/842). diff --git a/comet-cache.php b/comet-cache.php index 361bebd..724e1bc 100644 --- a/comet-cache.php +++ b/comet-cache.php @@ -1,6 +1,6 @@ =5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2016-10-08 15:01:37" + }, + { + "name": "guzzlehttp/promises", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "2693c101803ca78b27972d84081d027fca790a1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2693c101803ca78b27972d84081d027fca790a1e", + "reference": "2693c101803ca78b27972d84081d027fca790a1e", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-11-18 17:47:58" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2016-06-24 23:00:38" + }, + { + "name": "mimmi20/wurfl-constants", + "version": "1.7.1.1", + "source": { + "type": "git", + "url": "https://github.com/mimmi20/wurfl-constants.git", + "reference": "d0bd0154120cb833dbdf8a8075d5f14bcc521e42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mimmi20/wurfl-constants/zipball/d0bd0154120cb833dbdf8a8075d5f14bcc521e42", + "reference": "d0bd0154120cb833dbdf8a8075d5f14bcc521e42", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "fabpot/php-cs-fixer": "^1.11", + "phpunit/phpunit": "^4.8|^5.0", + "squizlabs/php_codesniffer": "^2.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Wurfl\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "description": "the Constants extracted from Wurfl for PHP 5.3", + "homepage": "https://github.com/mimmi20/wurfl-constants", + "keywords": [ + "Wurfl", + "browser", + "http", + "parser", + "user agent", + "user-agent" + ], + "time": "2016-04-23 18:18:10" + }, + { + "name": "mimmi20/wurflcache", + "version": "1.7.1.1", + "source": { + "type": "git", + "url": "https://github.com/mimmi20/WurflCache.git", + "reference": "9fc307df74f782a879f4604ab99bf61ecfc165d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mimmi20/WurflCache/zipball/9fc307df74f782a879f4604ab99bf61ecfc165d4", + "reference": "9fc307df74f782a879f4604ab99bf61ecfc165d4", + "shasum": "" + }, + "require": { + "mimmi20/wurfl-constants": "^1.7", + "php": ">=5.3.3", + "symfony/filesystem": "^2.8|^3.0" + }, + "require-dev": { + "desarrolla2/cache": "^1.8", + "doctrine/cache": "^1.5", + "fabpot/php-cs-fixer": "^1.11", + "mikey179/vfsstream": "^1.3", + "phpunit/phpunit": "^4.8 || ^5.0", + "squizlabs/php_codesniffer": "^2.0", + "zendframework/zend-cache": "^2.5", + "zetacomponents/cache": "dev-master" + }, + "suggest": { + "desarrolla2/cache": "to use other caches handled by desarrolla", + "doctrine/cache": "to use other caches handled by doctrine", + "zendframework/zend-cache": "to use other caches handled by zend", + "zetacomponents/cache": "to use other caches handled by zeta" + }, + "type": "library", + "autoload": { + "psr-4": { + "WurflCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thomas Müller", + "homepage": "https://github.com/mimmi20", + "role": "Developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/mimmi20/WurflCache/graphs/contributors" + } + ], + "description": "the Cache Classes for the Wurfl PHP Library for PHP 5.3", + "homepage": "https://github.com/mimmi20/WurflCache", + "keywords": [ + "Wurfl", + "cache" + ], + "time": "2016-08-06 11:25:21" + }, + { + "name": "monolog/monolog", + "version": "1.22.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", + "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "~5.3" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2016-11-26 00:15:39" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06 14:39:51" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10 12:19:37" + }, + { + "name": "symfony/console", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "09d0fd33560e3573185a2ea17614e37ba38716c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/09d0fd33560e3573185a2ea17614e37ba38716c5", + "reference": "09d0fd33560e3573185a2ea17614e37ba38716c5", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2016-11-16 22:18:16" + }, + { + "name": "symfony/debug", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/9f923e68d524a3095c5a2ae5fc7220c7cbc12231", + "reference": "9f923e68d524a3095c5a2ae5fc7220c7cbc12231", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/class-loader": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2016-11-16 22:18:16" + }, + { + "name": "symfony/filesystem", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", + "reference": "8d4cf7561a5b17e5eb7a02b80d0b8f014a3796d4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2016-11-24 00:46:43" + }, + { + "name": "symfony/finder", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "4263e35a1e342a0f195c9349c0dee38148f8a14f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/4263e35a1e342a0f195c9349c0dee38148f8a14f", + "reference": "4263e35a1e342a0f195c9349c0dee38148f8a14f", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2016-11-03 08:11:03" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14 01:06:16" + }, + { + "name": "thadafinser/package-info", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/ThaDafinser/PackageInfo.git", + "reference": "3cc580385cf694ba83eda3bbf7ea716a2253ea5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ThaDafinser/PackageInfo/zipball/3cc580385cf694ba83eda3bbf7ea716a2253ea5a", + "reference": "3cc580385cf694ba83eda3bbf7ea716a2253ea5a", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "~5.6|~7.0" + }, + "require-dev": { + "composer/composer": "^1.2.0", + "phpunit/phpunit": "^5.4.7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageInfo\\Installer" + }, + "autoload": { + "psr-4": { + "PackageInfo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "ThaDafinser", + "email": "martin.keckeis1@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2016-08-09 11:14:48" + }, + { + "name": "thadafinser/user-agent-parser", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/ThaDafinser/UserAgentParser.git", + "reference": "d400dbcf9fe229ad431e2b1a6f01c2b3dfb9f791" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ThaDafinser/UserAgentParser/zipball/d400dbcf9fe229ad431e2b1a6f01c2b3dfb9f791", + "reference": "d400dbcf9fe229ad431e2b1a6f01c2b3dfb9f791", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.1", + "php": "~5.6|~7.0", + "thadafinser/package-info": "^1.0" + }, + "conflict": { + "browscap/browscap-php": "<3,>=4", + "donatj/phpuseragentparser": "<0.5,>=1", + "jenssegers/agent": "<2.3,>=3", + "mimmi20/wurfl": "<1.6.4,>=2", + "mobiledetect/mobiledetectlib": "<2.7.5,>=3", + "piwik/device-detector": "<3.6,>=4", + "sinergi/browser-detector": "<6,>=7", + "ua-parser/uap-php": "<3.4.3,>=4", + "whichbrowser/parser": "<2.0.10,>=3", + "woothee/woothee": "<1.2,>=2", + "zsxsoft/php-useragent": "<1.2,>=2" + }, + "require-dev": { + "browscap/browscap-php": "^3.0", + "donatj/phpuseragentparser": "^0.5.0", + "endorphin-studio/browser-detector": "^3.0", + "friendsofphp/php-cs-fixer": "^1.11", + "handsetdetection/php-apikit": "^4.1.1", + "jenssegers/agent": "^2.3", + "mimmi20/wurfl": "^1.7.1.1", + "mobiledetect/mobiledetectlib": "^2.7.5", + "phpunit/phpunit": "^5.4", + "piwik/device-detector": "^3.6", + "sinergi/browser-detector": "^6.0", + "ua-parser/uap-php": "^3.4.3", + "whichbrowser/parser": "^2.0.10", + "woothee/woothee": "^1.2", + "zsxsoft/php-useragent": ">=1.2,<1.4" + }, + "suggest": { + "browscap/browscap-php": "Needed to use Provider\\BrowscapPhp", + "donatj/phpuseragentparser": "Needed to use Provider\\DonatjUAParser", + "endorphin-studio/browser-detector": "Needed to use Provider\\Endorphin", + "handsetdetection/php-apikit": "Needed to use Provider\\HandsetDetection", + "jenssegers/agent": "Needed to use Provider\\JenssegersAgent", + "mimmi20/wurfl": "Needed to use Provider\\Wurfl", + "piwik/device-detector": "Needed to use Provider\\PiwikDeviceDetector", + "sinergi/browser-detector": "Needed to use Provider\\SinergiBrowserDetector", + "ua-parser/uap-php": "Needed to use Provider\\UAParser", + "whichbrowser/parser": "Needed to use Provider\\WhichBrowser", + "woothee/woothee": "Needed to use Provider\\Woothee", + "zsxsoft/php-useragent": "Needed to use Provider\\Zsxsoft" + }, + "type": "library", + "autoload": { + "psr-4": { + "UserAgentParser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "ThaDafinser", + "email": "martin.keckeis1@gmail.com" + } + ], + "description": "UserAgentParser abstraction for different parse providers", + "keywords": [ + "Browscap", + "Device detection", + "DeviceAtlas", + "NeutrinoApiCom", + "Rendering engine", + "UA parser", + "UdgerCom", + "User agent detection", + "User agent parser", + "UserAgentApiCom", + "UserAgentParser", + "UserAgentStringCom", + "WhatIsMyBrowserCom", + "Wurfl", + "bot detection", + "browser", + "detection", + "device", + "donatj", + "engine", + "get_browser", + "jenssegers", + "mobile detection", + "mobile detector", + "mobile device detection", + "operating system", + "os", + "parser", + "piwik", + "sinergi", + "sniffing", + "ua", + "ua-parser", + "uaparser", + "user agent", + "useragent", + "whichbrowser", + "woothee" + ], + "time": "2016-08-12 05:51:14" + }, { "name": "websharks/css-minifier", "version": "dev-master", @@ -63,23 +1030,23 @@ }, { "name": "websharks/html-compressor", - "version": "161108", + "version": "161208", "source": { "type": "git", "url": "https://github.com/websharks/html-compressor.git", - "reference": "73a047734ab585cfaaa3e54526a484b788d768f5" + "reference": "3725ba080783c6e33337ed833ed939734af43fb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/websharks/html-compressor/zipball/73a047734ab585cfaaa3e54526a484b788d768f5", - "reference": "73a047734ab585cfaaa3e54526a484b788d768f5", + "url": "https://api.github.com/repos/websharks/html-compressor/zipball/3725ba080783c6e33337ed833ed939734af43fb2", + "reference": "3725ba080783c6e33337ed833ed939734af43fb2", "shasum": "" }, "require": { "ext-curl": "*", "ext-mbstring": "*", "ext-openssl": "*", - "php": ">=5.3", + "php": ">=5.4", "websharks/css-minifier": "dev-master", "websharks/js-minifier": "dev-master" }, @@ -117,7 +1084,7 @@ "html", "websharks" ], - "time": "2016-11-08 20:56:04" + "time": "2016-12-08 12:22:10" }, { "name": "websharks/js-minifier", diff --git a/readme.txt b/readme.txt index 4c72fb2..6c91048 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,8 @@ === Comet Cache === -Stable tag: 161119 +Stable tag: 161214-RC Requires at least: 4.2 -Tested up to: 4.7-alpha +Tested up to: 4.8-alpha Text Domain: comet-cache License: GPLv2 or later @@ -415,4 +415,4 @@ Requires WordPress v4.2+. - **Bug Fix**: Fixed a "PHP Fatal error: Undefined class constant 'CACHE_PATH_NO_SCHEME'" introduced by the previous release (v160416). This issue only affected sites where Feed Caching was enabled (_Comet Cache → Plugin Options → RSS, RDF, and Atom Feeds_). Props to MassimoD and @emanwebdev for reporting. See [Issue #739](https://github.com/websharks/comet-cache/issues/739). -For older changelog history going back to 7+ years, please see [CHANGELOG.md](https://github.com/websharks/comet-cache/blob/master/CHANGELOG.md). +For older changelog history going back more than 7 years, please see [CHANGELOG.md](https://github.com/websharks/comet-cache/blob/master/CHANGELOG.md). diff --git a/src/includes/classes/Actions.php b/src/includes/classes/Actions.php index 1588e81..161a1dc 100644 --- a/src/includes/classes/Actions.php +++ b/src/includes/classes/Actions.php @@ -71,8 +71,7 @@ protected function wipeCache($args) { if (!is_multisite() || !$this->plugin->currentUserCanWipeCache()) { return; // Nothing to do. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $counter = $this->plugin->wipeCache(true); @@ -97,8 +96,7 @@ protected function clearCache($args) { if (!$this->plugin->currentUserCanClearCache()) { return; // Not allowed to clear. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $counter = $this->plugin->clearCache(true); @@ -112,7 +110,6 @@ protected function clearCache($args) wp_redirect($redirect_to).exit(); } - /** * Action handler. * @@ -124,8 +121,7 @@ protected function ajaxWipeCache($args) { if (!is_multisite() || !$this->plugin->currentUserCanWipeCache()) { return; // Nothing to do. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $counter = $this->plugin->wipeCache(true); @@ -138,8 +134,6 @@ protected function ajaxWipeCache($args) exit($response); // JavaScript will take it from here. } - - /** * Action handler. * @@ -151,8 +145,7 @@ protected function ajaxClearCache($args) { if (!$this->plugin->currentUserCanClearCache()) { return; // Not allowed to clear. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $counter = $this->plugin->clearCache(true); @@ -168,7 +161,6 @@ protected function ajaxClearCache($args) exit($response); // JavaScript will take it from here. } - @@ -196,10 +188,11 @@ protected function ajaxClearCache($args) */ protected function saveOptions($args) { + global $is_apache, $is_nginx; + if (!current_user_can($this->plugin->cap)) { return; // Nothing to do. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } if (!empty($_FILES[GLOBAL_NS]['tmp_name']['import_options'])) { @@ -213,10 +206,11 @@ protected function saveOptions($args) unset($args['last_pro_stats_log']); // CANNOT be imported! } + $args = $this->plugin->trimDeep(stripslashes_deep((array) $args)); $this->plugin->updateOptions($args); // Save/update options. - // Ensures `autoCacheMaybeClearPrimaryXmlSitemapError()` always validates the XML Sitemap when saving options (when applicable) + // Ensures `autoCacheMaybeClearPrimaryXmlSitemapError()` always validates the XML Sitemap when saving options (when applicable). delete_transient(GLOBAL_NS.'-'.md5($this->plugin->options['auto_cache_sitemap_url'])); $redirect_to = self_admin_url('/admin.php'); // Redirect preparations. @@ -224,8 +218,6 @@ protected function saveOptions($args) $this->plugin->autoWipeCache(); // May produce a notice. - global $is_apache, $is_nginx; - if ($this->plugin->options['enable']) { if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1'; @@ -233,17 +225,21 @@ protected function saveOptions($args) if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1'; } - if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { + if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) + && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1'; } if (!($add_advanced_cache = $this->plugin->addAdvancedCache())) { $query_args[GLOBAL_NS.'_advanced_cache_add_failure'] = $add_advanced_cache === null ? 'advanced-cache' : '1'; } + if (!$this->plugin->options['auto_cache_enable']) { - $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPhpReqsError()` + // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPhpReqsError()`. + $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); } if (!$this->plugin->options['auto_cache_enable'] || !$this->plugin->options['auto_cache_sitemap_url']) { - $this->plugin->dismissMainNotice('xml_sitemap_missing'); // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPrimaryXmlSitemapError()` + // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPrimaryXmlSitemapError()`. + $this->plugin->dismissMainNotice('xml_sitemap_missing'); } $this->plugin->updateBlogPaths(); // Multisite networks only. } else { @@ -256,8 +252,11 @@ protected function saveOptions($args) if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) { $query_args[GLOBAL_NS.'_advanced_cache_remove_failure'] = '1'; } - $this->plugin->dismissMainNotice('xml_sitemap_missing'); // Dismiss notice when disabling plugin - $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); // Dismiss notice when disabling plugin + // Dismiss notice when disabling plugin. + $this->plugin->dismissMainNotice('xml_sitemap_missing'); + + // Dismiss notice when disabling plugin. + $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); } $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to); @@ -273,24 +272,22 @@ protected function saveOptions($args) */ protected function restoreDefaultOptions($args) { + global $is_apache, $is_nginx; + if (!current_user_can($this->plugin->cap)) { return; // Nothing to do. - } - if (is_multisite() && !current_user_can($this->plugin->network_cap)) { + } elseif (is_multisite() && !current_user_can($this->plugin->network_cap)) { return; // Nothing to do. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $this->plugin->restoreDefaultOptions(); // Restore defaults. - $redirect_to = self_admin_url('/admin.php'); // Redirect preparations. + $redirect_to = self_admin_url('/admin.php'); // Redirect prep. $query_args = ['page' => GLOBAL_NS, GLOBAL_NS.'_restored' => '1']; $this->plugin->autoWipeCache(); // May produce a notice. - global $is_apache, $is_nginx; - if ($this->plugin->options['enable']) { if (!($add_wp_cache_to_wp_config = $this->plugin->addWpCacheToWpConfig())) { $query_args[GLOBAL_NS.'_wp_config_wp_cache_add_failure'] = '1'; @@ -298,12 +295,22 @@ protected function restoreDefaultOptions($args) if ($is_apache && !($add_wp_htaccess = $this->plugin->addWpHtaccess())) { $query_args[GLOBAL_NS.'_wp_htaccess_add_failure'] = '1'; } - if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { + if ($is_nginx && $this->plugin->applyWpFilters(GLOBAL_NS.'_wp_htaccess_nginx_notice', true) + && (!isset($_SERVER['WP_NGINX_CONFIG']) || $_SERVER['WP_NGINX_CONFIG'] !== 'done')) { $query_args[GLOBAL_NS.'_wp_htaccess_nginx_notice'] = '1'; } if (!($add_advanced_cache = $this->plugin->addAdvancedCache())) { $query_args[GLOBAL_NS.'_advanced_cache_add_failure'] = $add_advanced_cache === null ? 'advanced-cache' : '1'; } + + if (!$this->plugin->options['auto_cache_enable']) { + // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPhpReqsError()`. + $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); + } + if (!$this->plugin->options['auto_cache_enable'] || !$this->plugin->options['auto_cache_sitemap_url']) { + // Dismiss and check again on `admin_init` via `autoCacheMaybeClearPrimaryXmlSitemapError()`. + $this->plugin->dismissMainNotice('xml_sitemap_missing'); + } $this->plugin->updateBlogPaths(); // Multisite networks only. } else { if (!($remove_wp_cache_from_wp_config = $this->plugin->removeWpCacheFromWpConfig())) { @@ -315,6 +322,11 @@ protected function restoreDefaultOptions($args) if (!($remove_advanced_cache = $this->plugin->removeAdvancedCache())) { $query_args[GLOBAL_NS.'_advanced_cache_remove_failure'] = '1'; } + // Dismiss notice when disabling plugin. + $this->plugin->dismissMainNotice('xml_sitemap_missing'); + + // Dismiss notice when disabling plugin. + $this->plugin->dismissMainNotice('auto_cache_engine_minimum_requirements'); } $redirect_to = add_query_arg(urlencode_deep($query_args), $redirect_to); @@ -334,8 +346,7 @@ protected function dismissNotice($args) { if (!current_user_can($this->plugin->cap)) { return; // Nothing to do. - } - if (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { + } elseif (empty($_REQUEST['_wpnonce']) || !wp_verify_nonce($_REQUEST['_wpnonce'])) { return; // Unauthenticated POST data. } $args = $this->plugin->trimDeep(stripslashes_deep((array) $args)); diff --git a/src/includes/classes/ApiBase.php b/src/includes/classes/ApiBase.php index db09b87..e8ee8f8 100644 --- a/src/includes/classes/ApiBase.php +++ b/src/includes/classes/ApiBase.php @@ -44,6 +44,8 @@ public static function options() return $GLOBALS[GLOBAL_NS]->options; } + + /** * Purges expired cache files, leaving all others intact. * diff --git a/src/includes/classes/MenuPageOptions.php b/src/includes/classes/MenuPageOptions.php index ecc5a8c..cb944e6 100644 --- a/src/includes/classes/MenuPageOptions.php +++ b/src/includes/classes/MenuPageOptions.php @@ -163,6 +163,11 @@ public function __construct() echo ' '.__('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.', 'comet-cache')."\n"; echo ''."\n"; } + if (!empty($_REQUEST[GLOBAL_NS.'_ua_info_dir_population_failure'])) { + echo '
'."\n"; + echo ' '.sprintf(__('Failed to populate User-Agent detection files for Mobile-Adaptive Mode. User-Agent detection files are pulled from a remote location so you\'ll always have the most up-to-date information needed for accurate detection. However, it appears the remote source of this information is currently unvailable. Please wait 15 minutes, then try saving your %1$s options again.', 'comet-cache'), esc_html(NAME))."\n"; + echo '
'."\n"; + } if (!IS_PRO && $this->plugin->isProPreview()) { echo '
'."\n"; echo ''.__('close', 'comet-cache').' '."\n"; @@ -234,10 +239,19 @@ public function __construct() echo '

'.sprintf(__('License Key', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'."\n"; + if ((!defined('DISALLOW_FILE_MODS') || !DISALLOW_FILE_MODS) && !apply_filters('automatic_updater_disabled', defined('AUTOMATIC_UPDATER_DISABLED') && AUTOMATIC_UPDATER_DISABLED)) { + // See: and note that `automatic_updater_disabled` is a core filter. + echo '
'."\n"; + echo '

'.__('Automatic Background Updates', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('Enable this if you\'d like %1$s to download and install bug fixes and security updates automatically in the background. Requires a valid license key in the field above.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'."\n"; + } echo '
'."\n"; echo '

'.__('Beta Program', 'comet-cache').'

'."\n"; echo '

'.sprintf(__('If you would like to participate in our beta program and receive new features and bug fixes before they are released to the public, %1$s can include Release Candidates when checking for automatic updates. Release Candidates are almost-ready-for-production and have already been through many internal test runs. Our team runs the latest Release Candidate on all of our production sites, but that doesn\'t mean you\'ll want to do the same. :-) Please report any issues with Release Candidates on GitHub.', 'comet-cache'), esc_html(NAME)).'

'."\n"; - echo '

'."\n"; echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->clear_min_cap : 'edit_posts')).'

'."\n"; - echo '

'."\n"; echo '
'."\n"; } else { echo '
'."\n"; @@ -314,7 +327,6 @@ public function __construct() echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'."\n"; echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->clear_min_cap : 'edit_posts')).'

'."\n"; - echo '

'."\n"; echo '
'."\n"; } if ($this->plugin->functionIsPossible('opcache_reset')) { @@ -447,7 +459,7 @@ public function __construct() echo ' '."\n"; echo '

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

'.__('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.', 'comet-cache').'

'."\n"; + echo '

'.__('XML Sitemap Patterns (one per line): 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. XML Sitemap Pattern searches are performed against the REQUEST_URI. A wildcard ** (double asterisk) can be used when necessary; e.g., /sitemap**.xml. Note that ** = 0 or more characters of any kind, including / slashes. * (a single asterisk) means 0 or more characters that are NOT a slash /. Your patterns must match from beginning to end; i.e., the special chars: ^ (beginning of string) and $ (end of the string) are always on for these patterns (i.e., applied internally). For that reason, if you want to match part of a URI, use ** to match anything before and/or after the fragment you\'re searching for. For example, **/sitemap**.xml will match any URI containing /sitemap (anywhere), so long as the URI also ends with .xml. On the other hand, /sitemap*.xml will only match URIs that begin with /sitemap, and it will only match URIs ending in .xml in that immediate directory — bypassing any inside nested sub-directories. To learn more about this syntax, please see this KB article.', 'comet-cache').'

'."\n"; echo '

'."\n"; if (is_multisite()) { echo '

'.__('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.', 'comet-cache').'

'."\n"; @@ -457,10 +469,10 @@ public function __construct() if (IS_PRO || $this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.__('Misc. Auto-Clear Options', 'comet-cache').'

'."\n"; - echo '

'.__('Auto-Clear a List of Custom URLs Too?', 'comet-cache').'

'."\n"; - echo '

'.sprintf(__('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., https://example.com/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.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'.__('Auto-Clear Custom URL Patterns Too?', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('Auto-Clear Custom URL Patterns (one per line): When you update a Post/Page, approve a Comment, etc., %1$s will detect that a Post/Page cache should be cleared to keep your site up-to-date. When this occurs, %1$s can also clear a list of custom URLs that you enter here. Please list one URL per line. A wildcard * character can be used when necessary; e.g., https://example.com/category/abc/**. Note that ** (double asterisk) means 0 or more characters of any kind, including / slashes. * (a single asterisk) means 0 or more characters that are NOT a slash /. Your patterns must match from beginning to end; i.e., the special chars: ^ (beginning of string) and $ (end of the string) are always on for these patterns (i.e., applied internally). For that reason, if you want to match part of a URL, use ** to match anything before and/or after the fragment you\'re searching for. For example, https://**/category/abc/** will find all URLs containing /category/abc/ (anywhere); whereas https://*/category/abc/* will match URLs on any domain, but the path must then begin with /category/abc/ and the pattern will only match paths in that immediate directory — bypassing any additional paths in sub-directories. To learn more about this syntax, please see this KB article.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'."\n"; - echo '

'.__('Note: Relative URLs (e.g., /name-of-post) should NOT be used. Each entry above should start with http:// or https:// and include a fully qualified domain name.', 'comet-cache').'

'."\n"; + echo '

'.__('Note: Relative URLs (e.g., /name-of-post) should NOT be used. Each entry above should start with http:// or https:// and include a fully qualified domain name (or wildcard characters in your pattern that will match the domain).', 'comet-cache').'

'."\n"; } echo '
'."\n"; @@ -501,7 +513,6 @@ public function __construct() echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'."\n"; echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->stats_min_cap : 'edit_posts')).'

'."\n"; - echo '

'."\n"; echo ' '."\n"; } else { echo '
'."\n"; @@ -509,7 +520,6 @@ public function __construct() echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(NAME)).'

'."\n"; echo '

'."\n"; echo '

'.sprintf(__('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.', 'comet-cache'), esc_html(IS_PRO ? $this->plugin->stats_min_cap : 'edit_posts')).'

'."\n"; - echo '

'."\n"; echo '
'."\n"; } echo ' '."\n"; @@ -941,6 +951,10 @@ public function __construct() echo ' '."\n"; echo ' '."\n"; echo '

'."\n"; + echo '

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

'.__('CSS Exclusion Patterns?', 'comet-cache').'

'."\n"; echo '

'.__('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.', 'comet-cache').'

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

'.sprintf(__('Enable the Pro Preview to see Leverage Browser Caching, Enforce Canonical URLs, and more!', 'comet-cache'), esc_attr(add_query_arg(urlencode_deep(['page' => GLOBAL_NS, GLOBAL_NS.'_pro_preview' => '1']), self_admin_url('/admin.php')))).'

'."\n"; } - if (IS_PRO || $this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.__('Leverage Browser Caching?', 'comet-cache').'

'."\n"; @@ -1118,7 +1131,6 @@ public function __construct() echo '
'.esc_html($this->plugin->fillReplacementCodes(file_get_contents(dirname(__DIR__).'/templates/htaccess/browser-caching-enable.txt'))).'
'."\n"; echo ' '."\n"; } - if ((IS_PRO && !empty($GLOBALS['wp_rewrite']->permalink_structure)) || $this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.__('Enforce Canonical URLs?', 'comet-cache').'

'."\n"; @@ -1137,7 +1149,6 @@ public function __construct() } echo ' '."\n"; } - if ((IS_PRO && $this->plugin->options['cdn_enable']) || $this->plugin->isProPreview()) { echo '
'."\n"; echo '

'.__('Send Access-Control-Allow-Origin Header?', 'comet-cache').'

'."\n"; @@ -1158,8 +1169,51 @@ public function __construct() echo ' '."\n"; echo ''."\n"; } + /* ----------------------------------------------------------------------------------------- */ + + if (IS_PRO || $this->plugin->isProPreview()) { + echo '
'."\n"; - /* ----------------------------------------------------------------------------------------- */ + echo ' '."\n"; + echo ' '.__('Mobile Mode', 'comet-cache')."\n"; + echo ' '."\n"; + + echo '
'."\n"; + echo '

'.__(' Enable Mobile-Adaptive Mode?', 'comet-cache').'

'."\n"; + echo '

'.__('Tip: Generally speaking, you should only enable this if your WordPress theme uses an \'Adaptive\' design, as opposed to a design that\'s \'Responsive\'—the way most WordPress themes are built.', 'comet-cache').'

'."\n"; + echo '

'."\n"; + + if (!version_compare(PHP_VERSION, '5.6', '>=')) { + echo '

'.sprintf(__('PHP Version: This feature requires PHP v5.6 (or higher). You\'re currently running PHP v%1$s. Please contact your web hosting company for assistance.', 'comet-cache'), esc_html(PHP_VERSION)).'

'."\n"; + } + + echo '

'.__('What\'s the Difference Between Responsive and Adaptive?', 'comet-cache').'

'."\n"; + echo '

'.__('Responsive and Adaptive designs both attempt to optimize the user experience across different devices, adjusting for different viewport sizes, resolutions, usage contexts, control mechanisms, and so on. Responsive design (common for WordPress sites) works on the principle of flexibility — a single fluid website that can look good on any device. Responsive websites use media queries, flexible grids, and responsive images to create a user experience that flexes and changes based on a multitude of factors. If you have a Responsive theme, you probably do NOT need to enable Mobile-Adaptive Mode.', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('Adaptive design detects the device and other features, and then it provides the appropriate feature and layout based on a predefined set of viewport sizes and other characteristics. Adaptive themes generally decide what to display based on a visitor\'s User-Agent (i.e., OS, device, browser, version). Since this design choice results in multiple versions of a page being served to visitors, based on the device they access the site with, it then becomes important to cache each of those variations separately. That way a visitor on an iPhone isn\'t accidentally shown the cached copy of a page that was originally viewed by another visitor who was on a desktop computer. If your theme uses an Adaptive design, you probably DO want to enable Mobile-Adaptive Mode in %1$s.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + + echo '

'."\n"; + echo '

'.__('Mobile-Adaptive Tokens', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('When %1$s runs in Mobile-Adaptive Mode and it detects that a device is Mobile (e.g., a phone, tablet), it needs to know which factors you\'d like to consider. Mobile-Adaptive Tokens make this easy. In the field below, please configure a list of Mobile-Adaptive Tokens that establish the important factors on your site. Each token must be separated by a + sign. You can use just one, or use them all. However, it\'s IMPORTANT to note: With each new token, you add additional permutations that can fragment the cache and eat up a lot of disk space. Enable and monitor Cache Statistics so you can keep an eye on this. See: %1$s → Plugin Options → Cache-Related Statistics', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '

'.__('The available Tokens are as follows:', 'comet-cache').'

'."\n"; + echo '
    '."\n"; + echo '
  • '.__('os.name e.g., iOS, Android, WinPhone10, WinPhone8.1, etc.', 'comet-cache').'
  • '."\n"; + echo '
  • '.__('device.type e.g., Tablet, Mobile Device, Mobile Phone, etc.', 'comet-cache').'
  • '."\n"; + echo '
  • '.__('browser.name e.g., Safari, Mobile Safari UIWebView, Chrome, Android WebView, Firefox, Edge Mobile, IEMobile, IE, Coast, etc.', 'comet-cache').'
  • '."\n"; + echo '
  • '.__('browser.version e.g., 55.0, 1.3, 9383242.2392, etc.we suggest avoiding this token because there are many permutations.', 'comet-cache').'
  • '."\n"; + echo '
'."\n"; + echo '

'."\n"; + echo '

'.sprintf(__('The suggested default value is: %2$s.
However, just: os.name + device.type is better, if that will do.', 'comet-cache'), esc_html(NAME), esc_html($this->plugin->default_options['mobile_adaptive_salt'])).'

'."\n"; + echo '

'.__('The special token: device.is_mobile (i.e., any mobile device, including tablets, excluding laptops) can be used by itself. For example, if you simply want to break the cache down into mobile vs. NOT mobile.', 'comet-cache').'

'."\n"; + echo '

'.sprintf(__('Note: The underlying logic behind mobile detection is accomplished using a faster, precompiled version of Browscap Lite, and Browcap data is automatically updated (and recompiled) whenever you save %1$s options and/or when upgrading %1$s to a new version.', 'comet-cache'), esc_html(NAME)).'

'."\n"; + echo '
'."\n"; + + echo '
'."\n"; + echo '
'."\n"; + } + /* ----------------------------------------------------------------------------------------- */ if (IS_PRO || $this->plugin->isProPreview()) { echo '
'."\n"; diff --git a/src/includes/classes/Plugin.php b/src/includes/classes/Plugin.php index c6e65ed..95154ff 100644 --- a/src/includes/classes/Plugin.php +++ b/src/includes/classes/Plugin.php @@ -51,7 +51,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var bool If `FALSE`, run without hooks. + * @type bool If `FALSE`, run without hooks. */ public $enable_hooks = true; @@ -60,7 +60,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var array Pro-only option keys. + * @type array Pro-only option keys. */ public $pro_only_option_keys = []; @@ -69,7 +69,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var array Default options. + * @type array Default options. */ public $default_options = []; @@ -78,7 +78,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var array Configured options. + * @type array Configured options. */ public $options = []; @@ -87,7 +87,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var string WordPress capability. + * @type string WordPress capability. */ public $cap = 'activate_plugins'; @@ -96,7 +96,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var string WordPress capability. + * @type string WordPress capability. */ public $update_cap = 'update_plugins'; @@ -105,7 +105,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var string WordPress capability. + * @type string WordPress capability. */ public $network_cap = 'manage_network_plugins'; @@ -114,7 +114,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var string WordPress capability. + * @type string WordPress capability. */ public $uninstall_cap = 'delete_plugins'; @@ -127,7 +127,7 @@ class Plugin extends AbsBaseAp * * @since 150422 Rewrite. * - * @var string Cache directory; relative to the configured base directory. + * @type string Cache directory; relative to the configured base directory. */ public $cache_sub_dir = 'cache'; @@ -135,6 +135,8 @@ class Plugin extends AbsBaseAp + + /** * Plugin constructor. * @@ -196,6 +198,9 @@ public function setup() 'when_logged_in', 'version_salt', + 'mobile_adaptive_salt', + 'mobile_adaptive_salt_enable', + 'ua_info_last_data_update', 'htmlc_enable', 'htmlc_css_exclusions', @@ -210,6 +215,7 @@ public function setup() 'htmlc_compress_css_code', 'htmlc_compress_js_code', 'htmlc_compress_html_code', + 'htmlc_amp_exclusions_enable', 'htmlc_when_logged_in', 'auto_cache_enable', @@ -254,6 +260,8 @@ public function setup() 'pro_update_username', 'pro_update_password', + 'pro_auto_update_enable', + 'last_pro_stats_log', ]; $this->default_options = [ @@ -335,7 +343,15 @@ public function setup() /* Related to version salt. */ - 'version_salt' => '', // Any string value. + 'version_salt' => '', // Any string value as a cache path component. + + // This should be set to a `+` delimited string containing any of these tokens: + // `os.name + device.type + browser.name + browser.version` (version should be avoided). + // There is one additional token (`device.is_mobile`) that can be used stand-alone. + // i.e., to indicate that being mobile is the only factor worth considering. + 'mobile_adaptive_salt' => 'os.name + device.type + browser.name', + 'mobile_adaptive_salt_enable' => '0', // `0|1` Enable the mobile adaptive salt? + 'ua_info_last_data_update' => '0', // Timestamp. /* Related to HTML compressor. */ @@ -356,6 +372,7 @@ public function setup() 'htmlc_compress_css_code' => '1', // `0|1`. 'htmlc_compress_js_code' => '1', // `0|1`. 'htmlc_compress_html_code' => '1', // `0|1`. + 'htmlc_amp_exclusions_enable' => '1', // `0|1`. 'htmlc_when_logged_in' => '0', // `0|1`; enable when logged in? /* Related to auto-cache engine. */ @@ -427,6 +444,8 @@ public function setup() 'pro_update_username' => '', // Username. 'pro_update_password' => '', // Password or license key. + 'pro_auto_update_enable' => '0', // `0|1`; enable? + /* Related to stats logging. */ 'last_pro_stats_log' => '0', // Timestamp. @@ -494,6 +513,7 @@ public function setup() 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('update_option_sidebars_widgets', [$this, 'autoClearCache']); add_action('save_post', [$this, 'autoClearPostCache']); add_action('delete_post', [$this, 'autoClearPostCache']); @@ -523,6 +543,9 @@ public function setup() + add_action('delete_user', [$this, 'autoClearAuthorPageCacheOnUserDeletion'], 10, 2); + add_action('remove_user_from_blog', [$this, 'autoClearAuthorPageCacheOnUserDeletion'], 10, 1); + if ($this->options['enable'] && $this->applyWpFilters(GLOBAL_NS.'_disable_akismet_comment_nonce', true)) { 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 @@ -530,6 +553,7 @@ public function setup() } + /* -------------------------------------------------------------- */ if (!is_multisite() || is_main_site()) { // Main site only. diff --git a/src/includes/classes/VsUpgrades.php b/src/includes/classes/VsUpgrades.php index 109ae45..9deeeaa 100644 --- a/src/includes/classes/VsUpgrades.php +++ b/src/includes/classes/VsUpgrades.php @@ -57,7 +57,7 @@ protected function fromLte150807() if (version_compare($this->prev_version, '150807', '<=')) { delete_site_option(GLOBAL_NS.'_errors'); // No longer necessary. - if (is_multisite() && is_array($child_blogs = wp_get_sites())) { + if (is_multisite() && is_array($child_blogs = $this->plugin->getBlogs())) { $current_site = get_current_site(); // Current site. foreach ($child_blogs as $_child_blog) { switch_to_blog($_child_blog['blog_id']); diff --git a/src/includes/stub.php b/src/includes/stub.php index d03ce48..6a2fa8a 100644 --- a/src/includes/stub.php +++ b/src/includes/stub.php @@ -13,7 +13,7 @@ require_once dirname(__DIR__).'/vendor/autoload.php'; require_once __DIR__.'/functions/i18n-utils.php'; -${__FILE__}['version'] = '161119'; //version// +${__FILE__}['version'] = '161214-RC'; //version// ${__FILE__}['plugin'] = dirname(dirname(__DIR__)); ${__FILE__}['plugin'] .= '/'.basename(${__FILE__}['plugin']).'.php'; ${__FILE__}['ns_path'] = str_replace('\\', '/', __NAMESPACE__); // To dir/path. diff --git a/src/includes/templates/advanced-cache.x-php b/src/includes/templates/advanced-cache.x-php index 627f55f..df314c5 100644 --- a/src/includes/templates/advanced-cache.x-php +++ b/src/includes/templates/advanced-cache.x-php @@ -153,6 +153,7 @@ if (!defined('COMET_CACHE_DIR')) { */ define('COMET_CACHE_DIR', WP_CONTENT_DIR.'/'.'%%COMET_CACHE_DIR%%'); } + if (!defined('COMET_CACHE_MAX_AGE')) { /* * Cache expiration time. @@ -241,6 +242,9 @@ if (!defined('COMET_CACHE_404_CACHE_FILENAME')) { + + + $GLOBALS[GLOBAL_NS.'_advanced_cache'] = new Classes\AdvancedCache(); $GLOBALS[GLOBAL_NS.'__advanced_cache'] = &$GLOBALS[GLOBAL_NS.'_advanced_cache']; if (!isset($GLOBALS['zencache__advanced_cache'])) { diff --git a/src/includes/traits/Ac/ObUtils.php b/src/includes/traits/Ac/ObUtils.php index af4e182..3bdc68f 100644 --- a/src/includes/traits/Ac/ObUtils.php +++ b/src/includes/traits/Ac/ObUtils.php @@ -6,92 +6,94 @@ trait ObUtils { /** - * Calculated protocol; one of `http://` or `https://`. + * Protocol. * * @since 150422 Rewrite. * - * @var float One of `http://` or `https://`. + * @type string Protocol */ public $protocol = ''; /** - * Host token for this request. + * Host token. * * @since 150821 Improving multisite compat. * - * @var string Host token for this request. + * @type string Host token. */ public $host_token = ''; /** - * Host base/dir tokens for this request. + * Host base/dir tokens. * * @since 150821 Improving multisite compat. * - * @var string Host base/dir tokens for this request. + * @type string Host base/dir tokens. */ public $host_base_dir_tokens = ''; + + /** - * Calculated version salt; set by site configuration data. + * Version salt. * * @since 150422 Rewrite. * - * @var string|mixed Any scalar value does fine. + * @type string Forced to a string. */ public $version_salt = ''; /** - * Relative cache path for the current request. + * Relative cache path. * * @since 150422 Rewrite. * - * @var string Cache path for the current request. + * @type string Cache path. */ public $cache_path = ''; /** - * Absolute cache file path for the current request. + * Absolute cache file path. * * @since 150422 Rewrite. * - * @var string Absolute cache file path for the current request. + * @type string Absolute cache file path. */ public $cache_file = ''; /** - * Relative 404 cache path for the current request. + * Relative 404 cache path. * * @since 150422 Rewrite. * - * @var string 404 cache path for the current request. + * @type string 404 cache path. */ public $cache_path_404 = ''; /** - * Absolute 404 cache file path for the current request. + * Absolute 404 cache file path. * * @since 150422 Rewrite. * - * @var string Absolute 404 cache file path for the current request. + * @type string Absolute 404 cache file path. */ public $cache_file_404 = ''; /** - * Version salt followed by the current request location. + * Version salt + location. * * @since 150422 Rewrite. * - * @var string Version salt followed by the current request location. + * @type string Version salt + location. */ public $salt_location = ''; /** - * Calculated max age; i.e., before expiration. + * Calculated max age. * - * @since 151002 Load average checks in pro version. + * @since 151002 Load average checks. * - * @var int Calculated max age; i.e., before expiration. + * @type int Calculated max age. */ public $cache_max_age = 0; @@ -100,17 +102,16 @@ trait ObUtils * * @since 161119 Calculated 12 hour expiration time. * - * @var int Calculated 12 hour expiration time. + * @type int Calculated 12 hour expiration time. */ public $nonce_cache_max_age = 0; /** - * Start output buffering (if applicable); or serve a cache file (if possible). - * - * @since 150422 Rewrite. + * Start output buffering or serve cache. * - * @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. + * @since 150422 Rewrite. 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. */ public function maybeStartOutputBuffering() { @@ -193,7 +194,9 @@ public function maybeStartOutputBuffering() $this->protocol = $this->isSsl() ? 'https://' : 'http://'; $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); @@ -362,11 +365,15 @@ public function outputBufferCallbackHandler($buffer, $phase) $DebugNotes->addAsciiArt(sprintf(__('%1$s Notes', 'comet-cache'), NAME)); $DebugNotes->addLineBreak(); - $DebugNotes->add(__('Cache File Version Salt', 'comet-cache'), $this->version_salt ? $this->version_salt : __('n/a', 'comet-cache')); - if (IS_PRO && COMET_CACHE_WHEN_LOGGED_IN && $this->user_token) { $DebugNotes->add(__('Cache File User Token', 'comet-cache'), $this->user_token); } + if (IS_PRO && COMET_CACHE_MOBILE_ADAPTIVE_SALT_ENABLE && COMET_CACHE_MOBILE_ADAPTIVE_SALT && $this->mobile_adaptive_salt) { + // Note: Not using `$this->mobile_adaptive_salt` here. Instead, generating a human readable variation. + $DebugNotes->add(__('Cache File for Mobile Device', 'comet-cache'), $this->fillUaTokens(COMET_CACHE_MOBILE_ADAPTIVE_SALT, false)); + } + $DebugNotes->add(__('Cache File Version Salt', 'comet-cache'), $this->version_salt ? $this->version_salt : __('n/a', 'comet-cache')); + $DebugNotes->addLineBreak(); $DebugNotes->add(__('Cache File URL', 'comet-cache'), $this->is_404 ? __('404 [error document]', 'comet-cache') : $this->protocol.$this->host_token.$_SERVER['REQUEST_URI']); diff --git a/src/includes/traits/Plugin/InstallUtils.php b/src/includes/traits/Plugin/InstallUtils.php index b211336..3a59728 100644 --- a/src/includes/traits/Plugin/InstallUtils.php +++ b/src/includes/traits/Plugin/InstallUtils.php @@ -35,6 +35,7 @@ public function activate() $this->addWpHtaccess(); $this->addAdvancedCache(); $this->updateBlogPaths(); + $this->autoClearCache(); } @@ -48,22 +49,28 @@ public function activate() public function checkVersion() { $prev_version = $this->options['version']; + if (version_compare($prev_version, VERSION, '>=')) { return; // Nothing to do; up-to-date. } - $this->options = $this->getOptions(false, true); // Don't discard options not present in $this->default_options, and DO force-pull options directly from get_site_option() + $this->options = $this->getOptions(false, true); + // Don't discard options not present in $this->default_options, + // and DO force-pull options directly from get_site_option(). - $this->updateOptions(['version' => VERSION], false); // Retain all options in database for VS Upgrade routine + $this->updateOptions(['version' => VERSION], false); + // Retain all options in database for VS Upgrade routine. new Classes\VsUpgrades($prev_version); - $this->updateOptions(['version' => VERSION], true); // Discard options not present in $this->default_options + $this->updateOptions(['version' => VERSION], true); + // Discard options not present in $this->default_options. if ($this->options['enable']) { $this->addWpCacheToWpConfig(); $this->addWpHtaccess(); $this->addAdvancedCache(); $this->updateBlogPaths(); + } $this->wipeCache(); // Fresh start now. @@ -103,11 +110,9 @@ public function uninstall() if (!defined('WP_UNINSTALL_PLUGIN')) { return; // Disallow. - } - if (empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) { + } elseif (empty($GLOBALS[GLOBAL_NS.'_uninstalling'])) { return; // Not uninstalling. - } - if (!current_user_can($this->uninstall_cap)) { + } elseif (!current_user_can($this->uninstall_cap)) { return; // Extra layer of security. } $this->removeWpCacheFromWpConfig(); @@ -149,38 +154,27 @@ public function addWpCacheToWpConfig() { if (!$this->options['enable']) { return ''; // Nothing to do. - } - if (!($wp_config_file = $this->findWpConfigFile())) { + } elseif (!($wp_config_file = $this->findWpConfigFile())) { return ''; // Unable to find `/wp-config.php`. - } - if (!is_readable($wp_config_file)) { + } elseif (!is_readable($wp_config_file)) { return ''; // Not possible. - } - if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { + } elseif (!($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))) { + } elseif (!($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*;/ui', $wp_config_file_contents_no_whitespace)) { + } elseif (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[1-9][0-9\.]*|true|([\'"])(?:[^0\'"]|[^\'"]{2,})\\2)\s*\)\s*;/ui', $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())) { + } elseif (!($wp_config_file_contents = $this->removeWpCacheFromWpConfig())) { return ''; // Unable to remove previous value. - } - if (!($wp_config_file_contents = preg_replace('/^\s*(\<\?php|\<\?)\s+/ui', '${1}'."\n"."define( 'WP_CACHE', true );"."\n", $wp_config_file_contents, 1))) { + } elseif (!($wp_config_file_contents = preg_replace('/^\s*(\<\?php|\<\?)\s+/ui', '${1}'."\n"."define( 'WP_CACHE', true );"."\n", $wp_config_file_contents, 1))) { return ''; // Failure; something went terribly wrong here. - } - if (mb_strpos($wp_config_file_contents, "define( 'WP_CACHE', true );") === false) { + } elseif (mb_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) { + } elseif (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { return ''; // We may NOT edit any files. - } - if (!is_writable($wp_config_file)) { + } elseif (!is_writable($wp_config_file)) { return ''; // Not possible. - } - if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { + } elseif (!file_put_contents($wp_config_file, $wp_config_file_contents)) { return ''; // Failure; could not write changes. } return $wp_config_file_contents; @@ -198,35 +192,25 @@ public function removeWpCacheFromWpConfig() { if (!($wp_config_file = $this->findWpConfigFile())) { return ''; // Unable to find `/wp-config.php`. - } - if (!is_readable($wp_config_file)) { + } elseif (!is_readable($wp_config_file)) { return ''; // Not possible. - } - if (!($wp_config_file_contents = file_get_contents($wp_config_file))) { + } elseif (!($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))) { + } elseif (!($wp_config_file_contents_no_whitespace = php_strip_whitespace($wp_config_file))) { return ''; // Failure; file empty - } - if (!preg_match('/([\'"])WP_CACHE\\1/ui', $wp_config_file_contents_no_whitespace)) { + } elseif (!preg_match('/([\'"])WP_CACHE\\1/ui', $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*;/ui', $wp_config_file_contents_no_whitespace) && !is_writable($wp_config_file)) { + } elseif (preg_match('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:0|FALSE|NULL|([\'"])0?\\2)\s*\)\s*;/ui', $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*;/ui', '', $wp_config_file_contents))) { + } elseif (!($wp_config_file_contents = preg_replace('/\bdefine\s*\(\s*([\'"])WP_CACHE\\1\s*,\s*(?:\-?[0-9\.]+|TRUE|FALSE|NULL|([\'"])[^\'"]*\\2)\s*\)\s*;/ui', '', $wp_config_file_contents))) { return ''; // Failure; something went terribly wrong here. - } - if (preg_match('/([\'"])WP_CACHE\\1/ui', $wp_config_file_contents)) { + } elseif (preg_match('/([\'"])WP_CACHE\\1/ui', $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) { + } elseif (defined('DISALLOW_FILE_MODS') && DISALLOW_FILE_MODS) { return ''; // We may NOT edit any files. - } - if (!is_writable($wp_config_file)) { + } elseif (!is_writable($wp_config_file)) { return ''; // Not possible. - } - if (!file_put_contents($wp_config_file, $wp_config_file_contents)) { + } elseif (!file_put_contents($wp_config_file, $wp_config_file_contents)) { return ''; // Failure; could not write changes. } return $wp_config_file_contents; @@ -253,8 +237,7 @@ public function checkAdvancedCache() { if (!$this->options['enable']) { return; // Nothing to do. - } - if (!empty($_REQUEST[GLOBAL_NS])) { + } elseif (!empty($_REQUEST[GLOBAL_NS])) { return; // Skip on plugin actions. } $cache_dir = $this->cacheDir(); @@ -294,20 +277,20 @@ public function addAdvancedCache() 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))) { + } elseif (!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)) { + } elseif (!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))) { + } elseif (!($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), + + + ] ); @@ -345,8 +328,7 @@ public function addAdvancedCache() $_value, $advanced_cache_contents ); - } - unset($_option, $_value, $_values, $_response); // Housekeeping. + } // unset($_option, $_value, $_values, $_response); // Housekeeping. if (mb_strpos(PLUGIN_FILE, WP_CONTENT_DIR) === 0) { $plugin_file = "WP_CONTENT_DIR.'".$this->escSq(str_replace(WP_CONTENT_DIR, '', PLUGIN_FILE))."'"; @@ -406,11 +388,9 @@ public function removeAdvancedCache() if (!is_file($advanced_cache_file)) { return true; // Already gone. - } - if (is_readable($advanced_cache_file) && filesize($advanced_cache_file) === 0) { + } elseif (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)) { + } elseif (!is_writable($advanced_cache_file)) { return false; // Not possible. } /* Empty the file only. This way permissions are NOT lost in cases where @@ -474,11 +454,9 @@ public function checkBlogPaths() { if (!$this->options['enable']) { return; // Nothing to do. - } - if (!is_multisite()) { + } elseif (!is_multisite()) { return; // N/A. - } - if (!empty($_REQUEST[GLOBAL_NS])) { + } elseif (!empty($_REQUEST[GLOBAL_NS])) { return; // Skip on plugin actions. } $cache_dir = $this->cacheDir(); @@ -508,8 +486,7 @@ public function updateBlogPaths($enable_live_network_counts = null) if (!$this->options['enable']) { return $value; // Nothing to do. - } - if (!is_multisite()) { + } elseif (!is_multisite()) { return $value; // N/A. } $cache_dir = $this->cacheDir(); @@ -517,7 +494,8 @@ public function updateBlogPaths($enable_live_network_counts = null) $cache_lock = $this->cacheLock(); - clearstatcache(); + clearstatcache(); // Clear `stat()` cache. + if (!file_exists($cache_dir)) { mkdir($cache_dir, 0775, true); } @@ -538,7 +516,7 @@ public function updateBlogPaths($enable_live_network_counts = null) if (!$_path || $_path === '/') { unset($paths[$_key]); // Exclude main site. } - } + } // Must unset iteration by reference here. unset($_key, $_path); // Housekeeping. file_put_contents($blog_paths_file, serialize($paths)); @@ -548,6 +526,8 @@ public function updateBlogPaths($enable_live_network_counts = null) return $value; // Pass through untouched (always). } + + /** * Deletes base directory. * diff --git a/src/includes/traits/Plugin/WcpAuthorUtils.php b/src/includes/traits/Plugin/WcpAuthorUtils.php index 4130342..6b092a4 100644 --- a/src/includes/traits/Plugin/WcpAuthorUtils.php +++ b/src/includes/traits/Plugin/WcpAuthorUtils.php @@ -6,7 +6,7 @@ trait WcpAuthorUtils { /** - * Automatically clears cache files for the author page(s). + * Clears cache files for the author page(s). * * @attaches-to `post_updated` hook. * @@ -25,26 +25,21 @@ trait WcpAuthorUtils */ public function autoClearAuthorPageCache($post_id, \WP_Post $post_after, \WP_Post $post_before) { - $counter = 0; // Initialize. - $enqueued_notices = 0; // Initialize. - $authors = []; // Initialize. - $authors_to_clear = []; // Initialize. + $authors = $authors_to_clear = []; // Initialize. + $counter = $enqueued_notices = 0; // Initialize. - if (!($post_id = (integer) $post_id)) { + if (!($post_id = (int) $post_id)) { return $counter; // Nothing to do. - } - if (!is_null($done = &$this->cacheKey('autoClearAuthorPageCache', [$post_id, $post_after->ID, $post_before->ID]))) { + } elseif (($done = &$this->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']) { + } elseif (!$this->options['cache_clear_author_page_enable']) { return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $this->cacheDir())) { + } elseif (!is_dir($cache_dir = $this->cacheDir())) { return $counter; // Nothing to do. } /* @@ -58,25 +53,22 @@ public function autoClearAuthorPageCache($post_id, \WP_Post $post_after, \WP_Pos * * 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. + if ($post_after->post_author !== $post_before->post_author + && ($post_before->post_status === 'publish' || $post_before->post_status === 'private')) { + $authors[] = (int) $post_before->post_author; + $authors[] = (int) $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[] = (int) $post_after->post_author; } 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. + } // unset($_author_id); // Housekeeping. + if (!$authors_to_clear) { + return $counter; // Nothing to do. + } foreach ($authors_to_clear as $_author) { $_author_regex = $this->buildHostCachePathRegex($_author['posts_url']); $_author_counter = $this->clearFilesFromHostCacheDir($_author_regex); @@ -86,12 +78,72 @@ public function autoClearAuthorPageCache($post_id, \WP_Post $post_after, \WP_Pos $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. + } // unset($_author, $_author_regex, $_author_counter); // Housekeeping. $counter += $this->autoClearXmlFeedsCache('blog'); $counter += $this->autoClearXmlFeedsCache('post-authors', $post_id); return $counter; } + + /** + * Clears cache files for the author page(s). + * + * @attaches-to `remove_user_from_blog` hook. + * @attaches-to `delete_user` hook. + * + * @since 16xxxx Adding support for user deletions. + * + * @param int $user_id A WordPress user ID. + * @param int $rat_user_id User ID (reassign via `delete_user` hook). + * + * @throws \Exception If a clear failure occurs. + * + * @return int Total files cleared by this routine (if any). + */ + public function autoClearAuthorPageCacheOnUserDeletion($user_id, $rat_user_id = 0) + { + $authors_to_clear = []; // Initialize. + $rat_user_id = (int) $rat_user_id; + $counter = $enqueued_notices = 0; + + if (!($user_id = (int) $user_id)) { + return $counter; // Nothing to do. + } elseif (($done = &$this->cacheKey('autoClearAuthorPageCacheOnUserDeletion', [$user_id, $rat_user_id]))) { + return $counter; // Already did this. + } + $done = true; // Flag as having been done. + + if (!$this->options['enable']) { + return $counter; // Nothing to do. + } elseif (!$this->options['cache_clear_author_page_enable']) { + return $counter; // Nothing to do. + } elseif (!is_dir($cache_dir = $this->cacheDir())) { + return $counter; // Nothing to do. + } + if (($WP_User = new \WP_User($user_id)) && $WP_User->exists() && $WP_User->has_cap('edit_posts')) { + $authors_to_clear[$WP_User->ID]['posts_url'] = get_author_posts_url($WP_User->ID); + $authors_to_clear[$WP_User->ID]['display_name'] = get_the_author_meta('display_name', $WP_User->ID); + } + if ($rat_user_id && ($rat_WP_User = new \WP_User($rat_user_id)) && $rat_WP_User->exists() && $rat_WP_User->has_cap('edit_posts')) { + $authors_to_clear[$rat_WP_User->ID]['posts_url'] = get_author_posts_url($rat_WP_User->ID); + $authors_to_clear[$rat_WP_User->ID]['display_name'] = get_the_author_meta('display_name', $rat_WP_User->ID); + } + if (!$authors_to_clear) { + return $counter; // Nothing to do. + } + 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. + } + // @TODO Consider clearing other cached locations here too. + } // unset($_author, $_author_regex, $_author_counter); // Housekeeping. + + return $counter; + } } diff --git a/src/includes/traits/Plugin/WcpFeedUtils.php b/src/includes/traits/Plugin/WcpFeedUtils.php index c381222..9927f47 100644 --- a/src/includes/traits/Plugin/WcpFeedUtils.php +++ b/src/includes/traits/Plugin/WcpFeedUtils.php @@ -27,23 +27,20 @@ public function autoClearXmlFeedsCache($type, $post_id = 0) if (!($type = (string) $type)) { return $counter; // Nothing we can do. } - $post_id = (integer) $post_id; // Force integer. + $post_id = (int) $post_id; // Force integer. - if (!is_null($done = &$this->cacheKey('autoClearXmlFeedsCache', [$type, $post_id]))) { + if (($done = &$this->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']) { + } elseif (!$this->options['feeds_enable']) { return $counter; // Nothing to do. - } - if (!$this->options['cache_clear_xml_feeds_enable']) { + } elseif (!$this->options['cache_clear_xml_feeds_enable']) { return $counter; // Nothing to do. - } - if (!is_dir($cache_dir = $this->cacheDir())) { + } elseif (!is_dir($cache_dir = $this->cacheDir())) { return $counter; // Nothing to do. } $utils = new Classes\FeedUtils(); // Feed utilities. @@ -106,12 +103,12 @@ public function autoClearXmlFeedsCache($type, $post_id = 0) 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. + } // 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]); diff --git a/src/includes/traits/Shared/BlogUtils.php b/src/includes/traits/Shared/BlogUtils.php index 8d4d10d..47ef5b1 100644 --- a/src/includes/traits/Shared/BlogUtils.php +++ b/src/includes/traits/Shared/BlogUtils.php @@ -5,6 +5,34 @@ trait BlogUtils { + /** + * Get child blogs. + * + * @since 16xxxx Replacing `wp_get_sites()`. + * + * @return array An array of child blogs (max 100). + * + * @note The return value of this function is NOT cached in support of `$GLOBALS['wpdb']->siteid`. + */ + public function getBlogs() + { + if (!is_multisite()) { + return []; // Not possible. + } + $sites = []; // Initialize. + + foreach (get_sites([ + 'number' => 100, 'count' => false, + 'network_id' => $GLOBALS['wpdb']->siteid, + ]) as $_site) { + if (($_site = get_site($_site))) { + $sites[] = $_site->to_array(); + } // For compatibiliey with old `wp_get_sites()`. + } // unset($_site); + + return $sites; + } + /** * Get blog details. * @@ -21,11 +49,11 @@ public function blogDetails($blog_id = 0) if (!is_multisite() || $this->isAdvancedCache()) { return null; // Not possible. } - if (($blog_id = (integer) $blog_id) < 0) { - $blog_id = (integer) get_current_site()->blog_id; + if (($blog_id = (int) $blog_id) < 0) { + $blog_id = (int) get_current_site()->blog_id; } if (!$blog_id) { - $blog_id = (integer) get_current_blog_id(); + $blog_id = (int) get_current_blog_id(); } if (!$blog_id || $blog_id < 0) { return null; // Not possible. diff --git a/src/includes/traits/Shared/CacheDirUtils.php b/src/includes/traits/Shared/CacheDirUtils.php index 93fbf0a..dc22135 100644 --- a/src/includes/traits/Shared/CacheDirUtils.php +++ b/src/includes/traits/Shared/CacheDirUtils.php @@ -106,6 +106,7 @@ public function purgeFilesFromHostCacheDir($regex) * @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). * * @@ -166,7 +167,6 @@ public function deleteFilesFromCacheDir($regex, $check_max_age = false) // 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())) { @@ -201,7 +201,7 @@ public function deleteFilesFromCacheDir($regex, $check_max_age = false) case 'dir': // A regular directory; i.e., not a symlink. - if ($regex !== '/^.+/i') { + if (!in_array(rtrim(str_replace(['^', '$'], '', $regex), 'ui'), ['/.*/', '/.+/'], true)) { break; // Not deleting everything. } if ($check_max_age && !empty($max_age)) { @@ -251,8 +251,8 @@ public function deleteFilesFromCacheDir($regex, $check_max_age = false) * @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). * + * @return int Total files deleted by this routine (if any). */ public function deleteFilesFromHostCacheDir( $regex, @@ -344,7 +344,6 @@ public function deleteFilesFromHostCacheDir( // 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())) { @@ -379,7 +378,7 @@ public function deleteFilesFromHostCacheDir( case 'dir': // A regular directory; i.e., not a symlink. - if ($regex !== '/^.+/i') { + if (!in_array(rtrim(str_replace(['^', '$'], '', $regex), 'ui'), ['/.*/', '/.+/'], true)) { break; // Not deleting everything. } if ($check_max_age && !empty($max_age)) { @@ -455,8 +454,8 @@ public function deleteFilesFromHostCacheDir( * @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). * + * @return int Total files/directories deleted by this routine (if any). */ public function deleteAllFilesDirsIn($dir, $delete_dir_too = false) { @@ -485,13 +484,12 @@ public function deleteAllFilesDirsIn($dir, $delete_dir_too = false) 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) { + foreach (($_dir_regex_iteration = $this->dirRegexIteration($dir_temp, '/.+/u')) 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)) { @@ -562,8 +560,8 @@ public function deleteAllFilesDirsIn($dir, $delete_dir_too = false) * @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). * + * @return int Total files/directories erased by this routine (if any). */ public function eraseAllFilesDirsIn($dir, $erase_dir_too = false) { @@ -584,13 +582,12 @@ public function eraseAllFilesDirsIn($dir, $erase_dir_too = false) } clearstatcache(); // Clear stat cache to be sure we have a fresh start below. - foreach (($_dir_regex_iteration = $this->dirRegexIteration($dir, '/.+/')) as $_resource) { + foreach (($_dir_regex_iteration = $this->dirRegexIteration($dir, '/.+/u')) 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)) { diff --git a/src/includes/traits/Shared/CachePathUtils.php b/src/includes/traits/Shared/CachePathUtils.php index bdce189..75a7e3a 100644 --- a/src/includes/traits/Shared/CachePathUtils.php +++ b/src/includes/traits/Shared/CachePathUtils.php @@ -63,8 +63,6 @@ public function cachePathRegexSuffixFrag($regex_suffix_frag = self::CACHE_PATH_R * @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() { @@ -181,7 +179,6 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = if (!($flags & $this::CACHE_PATH_NO_QUV)) { if (!($flags & $this::CACHE_PATH_NO_QUERY)) { if (isset($url_parts['query']) && $url_parts['query'] !== '') { - // Support for ignored GET vars. parse_str($url_parts['query'], $_query_vars); $_query_vars = $this->filterQueryVars($_query_vars); @@ -206,11 +203,11 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = $cache_path = trim(preg_replace(['/\/+/u', '/\.+/u'], ['/', '.'], $cache_path), '/'); if ($flags & $this::CACHE_PATH_ALLOW_WD_REGEX) { - $cache_path = preg_replace('/[^a-z0-9\/.*\^$]/ui', '-', $cache_path); + $cache_path = preg_replace('/[^a-z0-9\/.+*\^$]/ui', '-', $cache_path); } elseif ($flags & $this::CACHE_PATH_ALLOW_WILDCARDS) { - $cache_path = preg_replace('/[^a-z0-9\/.*]/ui', '-', $cache_path); + $cache_path = preg_replace('/[^a-z0-9\/.+*]/ui', '-', $cache_path); } else { - $cache_path = preg_replace('/[^a-z0-9\/.]/ui', '-', $cache_path); + $cache_path = preg_replace('/[^a-z0-9\/.+]/ui', '-', $cache_path); } if (!($flags & $this::CACHE_PATH_NO_EXT)) { $cache_path .= '.html'; @@ -224,7 +221,7 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = * @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, the final regex pattern will be `/^'.$regex_suffix_frag.'/ui`. * 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]+)[.\/])`. @@ -245,7 +242,7 @@ public function buildCachePathRegex($url, $regex_suffix_frag = self::CACHE_PATH_ $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'; + return '/^'.$cache_path_regex.$regex_suffix_frag.'/ui'; } /** @@ -254,7 +251,7 @@ public function buildCachePathRegex($url, $regex_suffix_frag = self::CACHE_PATH_ * @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, the final regex pattern will be `/^'.$regex_suffix_frag.'/ui`. * 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]+)[.\/])`. @@ -291,7 +288,7 @@ public function buildHostCachePathRegex($url, $regex_suffix_frag = self::CACHE_P $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'; + return '/^'.$abs_relative_cache_path_regex.$regex_suffix_frag.'/ui'; } /** @@ -303,7 +300,7 @@ public function buildHostCachePathRegex($url, $regex_suffix_frag = self::CACHE_P * 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, the final regex pattern will be `/^'.$regex_suffix_frag.'/ui`. * 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]+)[.\/])`. @@ -324,7 +321,7 @@ public function buildCachePathRegexFromWcUrl($url, $regex_suffix_frag = self::CA $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'; + return '/^'.$cache_path_regex.$regex_suffix_frag.'/ui'; } /** @@ -375,7 +372,7 @@ public function buildHostCachePathRegexFragsFromWcUris($uris, $regex_suffix_frag * @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, the final regex pattern will be `/^'.$regex_suffix_frag.'/ui`. * 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]+)[.\/])`. @@ -389,6 +386,6 @@ public function assembleCachePathRegex($regex_frag, $regex_suffix_frag = self::C $regex_frag = (string) $regex_frag; $regex_suffix_frag = $this->cachePathRegexSuffixFrag($regex_suffix_frag); - return '/^'.$regex_frag.$regex_suffix_frag.'/i'; + return '/^'.$regex_frag.$regex_suffix_frag.'/ui'; } } diff --git a/src/includes/traits/Shared/ConditionalUtils.php b/src/includes/traits/Shared/ConditionalUtils.php index e81cc6e..07f8870 100644 --- a/src/includes/traits/Shared/ConditionalUtils.php +++ b/src/includes/traits/Shared/ConditionalUtils.php @@ -321,7 +321,7 @@ public function isExtensionLoaded($extension) */ public function functionIsPossible($function) { - $function = (string) $function; + $function = mb_strtolower((string) $function); if (($is = &$this->staticKey(__FUNCTION__, $function)) !== null) { return $is; // Already cached this. @@ -335,17 +335,16 @@ public function functionIsPossible($function) if (($blacklist_functions = trim(ini_get('suhosin.executor.func.blacklist')))) { $disabled_functions = array_merge($disabled_functions, preg_split('/[\s;,]+/', mb_strtolower($blacklist_functions), -1, PREG_SPLIT_NO_EMPTY)); } + if (($opcache_restrict_api = trim(ini_get('opcache.restrict_api'))) && mb_stripos(__FILE__, $opcache_restrict_api) !== 0) { + $disabled_functions = array_merge($disabled_functions, ['opcache_compile_file', 'opcache_get_configuration', 'opcache_get_status', 'opcache_invalidate', 'opcache_is_script_cached', 'opcache_reset']); + } 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)) { - return $is = false; // Not possible. - } - } // In PHP, language constructs cannot be disabled/blacklisted whatsoever. - - if ($disabled_functions && in_array(mb_strtolower($function), $disabled_functions, true)) { + if ($disabled_functions && in_array($function, $disabled_functions, true)) { + return $is = false; // Not possible. + } elseif ((!function_exists($function) || !is_callable($function)) && !in_array($function, $this->php_constructs, true)) { return $is = false; // Not possible. } return $is = true; diff --git a/src/includes/traits/Shared/FsUtils.php b/src/includes/traits/Shared/FsUtils.php index 923f289..49f745e 100644 --- a/src/includes/traits/Shared/FsUtils.php +++ b/src/includes/traits/Shared/FsUtils.php @@ -151,8 +151,7 @@ public function dirRegexIteration($dir, $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. + if ($regex && !in_array(rtrim(str_replace(['^', '$'], '', $regex), 'ui'), ['/.*/', '/.+/'], true)) { // Apply regex filter? return new \RegexIterator($iterator_iterator, $regex, \RegexIterator::MATCH, \RegexIterator::USE_KEY); } return $iterator_iterator; // Iterate everything. @@ -171,7 +170,7 @@ public function dirRegexIteration($dir, $regex = '') public function bytesAbbr($bytes, $precision = 2) { $bytes = max(0.0, (float) $bytes); - $precision = max(0, (integer) $precision); + $precision = max(0, (int) $precision); $units = ['bytes', 'kbs', 'MB', 'GB', 'TB']; $power = floor(($bytes ? log($bytes) : 0) / log(1024));