← All changes
|
includes/abilities/maintenance/class-purge-caches.php
+56
-27
2.7.0
→
2.10.0
View file →
| @@ -44,8 +44,13 @@ | ||
| 44 | 44 | 'ai' => 'AI response cache', |
| 45 | 45 | 'integrations' => 'Google integration responses', |
| 46 | 46 | 'content_type' => 'Content Type Matrix resolution', |
| 47 | 47 | 'transients' => 'ThinkRank transients', |
| 48 | + // Not a cache in the same sense — it rebuilds a published file rather | |
| 49 | + // than dropping a stored value — but it is what a caller looking at a | |
| 50 | + // stale sitemap reaches for, and the docblock above already named that | |
| 51 | + // problem as a reason this ability exists (#764). | |
| 52 | + 'sitemap' => 'Served XML sitemap (rebuilt, not just cleared)', | |
| 48 | 53 | ]; |
| 49 | 54 | |
| 50 | 55 | /** |
| 51 | 56 | * Constructor. |
| @@ -52,9 +57,9 @@ | ||
| 52 | 57 | */ |
| 53 | 58 | public function __construct() { |
| 54 | 59 | $this->id = 'thinkrank/purge-caches'; |
| 55 | 60 | $this->label = __( 'Purge ThinkRank Caches', 'thinkrank' ); |
| 56 | - $this->description = __( 'Clear ThinkRank\'s cached data when it is showing something stale — the site audit, schema output, AI responses, Google integration responses, or all of them. Pass scopes to clear only what you need; omit it to clear everything. Nothing is deleted except caches: settings and content are untouched, and each cache rebuilds on the next request. After clearing the analyzer scope, use run-seo-analyzer to rebuild the audit immediately rather than waiting for the next request.', 'thinkrank' ); | |
| 61 | + $this->description = __( 'Clear ThinkRank\'s cached data when it is showing something stale — the site audit, schema output, AI responses, Google integration responses, or all of them. Pass scopes to clear only what you need; omit it to clear everything. Nothing is deleted except caches: settings and content are untouched, and each cache rebuilds on the next request. After clearing the analyzer scope, use run-seo-analyzer to rebuild the audit immediately rather than waiting for the next request. The "sitemap" scope is the exception to "rebuilds on the next request": the sitemap is a published file, so that scope rebuilds it there and then and reports whether it succeeded.', 'thinkrank' ); | |
| 57 | 62 | } |
| 58 | 63 | |
| 59 | 64 | /** |
| 60 | 65 | * {@inheritDoc} |
| @@ -114,8 +119,13 @@ | ||
| 114 | 119 | 'type' => 'array', |
| 115 | 120 | 'items' => [ 'type' => 'string' ], |
| 116 | 121 | 'description' => __( 'Scopes whose subsystem is not present on this install, so there was nothing to clear.', 'thinkrank' ), |
| 117 | 122 | ], |
| 123 | + 'failed' => [ | |
| 124 | + 'type' => 'array', | |
| 125 | + 'items' => [ 'type' => 'string' ], | |
| 126 | + 'description' => __( 'Scopes that exist but could not be cleared or rebuilt. For "sitemap" this means the served sitemap did not change, because another rebuild held the lock or the files could not be written or removed. Try again shortly; the Sitemap settings screen shows any recorded reason.', 'thinkrank' ), | |
| 127 | + ], | |
| 118 | 128 | ], |
| 119 | 129 | ]; |
| 120 | 130 | } |
| 121 | 131 | |
| @@ -130,75 +140,94 @@ | ||
| 130 | 140 | $requested = isset( $input['scopes'] ) && is_array( $input['scopes'] ) && ! empty( $input['scopes'] ) |
| 131 | 141 | ? array_values( array_intersect( array_keys( self::SCOPES ), array_map( 'strval', $input['scopes'] ) ) ) |
| 132 | 142 | : array_keys( self::SCOPES ); |
| 133 | 143 | |
| 134 | - $cleared = []; | |
| 135 | - $skipped = []; | |
| 144 | + $results = [ | |
| 145 | + 'cleared' => [], | |
| 146 | + 'skipped' => [], | |
| 147 | + 'failed' => [], | |
| 148 | + ]; | |
| 136 | 149 | |
| 137 | 150 | foreach ( $requested as $scope ) { |
| 138 | - if ( $this->purge( $scope ) ) { | |
| 139 | - $cleared[] = $scope; | |
| 140 | - } else { | |
| 141 | - $skipped[] = $scope; | |
| 142 | - } | |
| 151 | + $results[ $this->purge( $scope ) ][] = $scope; | |
| 143 | 152 | } |
| 144 | 153 | |
| 145 | - return [ | |
| 146 | - 'success' => true, | |
| 147 | - 'cleared' => $cleared, | |
| 148 | - 'skipped' => $skipped, | |
| 149 | - ]; | |
| 154 | + // `success` stays true when a scope failed: the call itself ran, and | |
| 155 | + // every other requested scope was still cleared. The failure is | |
| 156 | + // reported per scope in `failed`, which is where a caller has to look | |
| 157 | + // to know which cache is still stale. | |
| 158 | + return [ 'success' => true ] + $results; | |
| 150 | 159 | } |
| 151 | 160 | |
| 152 | 161 | /** |
| 153 | 162 | * Clear one scope. |
| 154 | 163 | * |
| 164 | + * Three outcomes rather than a bool. A bool had only "cleared" and | |
| 165 | + * "skipped" to map onto, and skipped is documented as "the subsystem is | |
| 166 | + * not present", so a sitemap rebuild that lost the lock or failed to write | |
| 167 | + * was reported to the caller as a sitemap that does not exist. | |
| 168 | + * | |
| 169 | + * @since 2.10.0 Returns 'cleared', 'skipped' or 'failed' instead of a bool. | |
| 170 | + * | |
| 155 | 171 | * @param string $scope Scope key. |
| 156 | - * @return bool True when the subsystem existed and was cleared. | |
| 172 | + * @return string 'cleared', 'skipped' (subsystem absent) or 'failed'. | |
| 157 | 173 | */ |
| 158 | - private function purge( string $scope ): bool { | |
| 174 | + private function purge( string $scope ): string { | |
| 159 | 175 | switch ( $scope ) { |
| 160 | 176 | case 'analyzer': |
| 161 | 177 | if ( ! class_exists( 'ThinkRank\\SEO\\SEO_Analyzer' ) ) { |
| 162 | - return false; | |
| 178 | + return 'skipped'; | |
| 163 | 179 | } |
| 164 | 180 | ( new \ThinkRank\SEO\SEO_Analyzer() )->flush_cache(); |
| 165 | - return true; | |
| 181 | + return 'cleared'; | |
| 166 | 182 | |
| 167 | 183 | case 'schema': |
| 168 | 184 | if ( ! class_exists( 'ThinkRank\\SEO\\Schema_Cache_Manager' ) ) { |
| 169 | - return false; | |
| 185 | + return 'skipped'; | |
| 170 | 186 | } |
| 171 | 187 | ( new \ThinkRank\SEO\Schema_Cache_Manager() )->clear_all(); |
| 172 | - return true; | |
| 188 | + return 'cleared'; | |
| 173 | 189 | |
| 174 | 190 | case 'ai': |
| 175 | 191 | if ( ! class_exists( 'ThinkRank\\AI\\Cache_Manager' ) ) { |
| 176 | - return false; | |
| 192 | + return 'skipped'; | |
| 177 | 193 | } |
| 178 | 194 | ( new \ThinkRank\AI\Cache_Manager() )->clear_all(); |
| 179 | - return true; | |
| 195 | + return 'cleared'; | |
| 180 | 196 | |
| 181 | 197 | case 'integrations': |
| 182 | 198 | if ( ! method_exists( 'ThinkRank\\API\\Integrations_Endpoint', 'purge_search_console_sites_cache' ) ) { |
| 183 | - return false; | |
| 199 | + return 'skipped'; | |
| 184 | 200 | } |
| 185 | 201 | \ThinkRank\API\Integrations_Endpoint::purge_search_console_sites_cache(); |
| 186 | - return true; | |
| 202 | + return 'cleared'; | |
| 187 | 203 | |
| 188 | 204 | case 'content_type': |
| 189 | 205 | if ( ! method_exists( 'ThinkRank\\SEO\\Content_Type_Settings', 'flush_cache' ) ) { |
| 190 | - return false; | |
| 206 | + return 'skipped'; | |
| 191 | 207 | } |
| 192 | 208 | \ThinkRank\SEO\Content_Type_Settings::flush_cache(); |
| 193 | - return true; | |
| 209 | + return 'cleared'; | |
| 194 | 210 | |
| 195 | 211 | case 'transients': |
| 196 | 212 | $this->purge_transients(); |
| 197 | - return true; | |
| 213 | + return 'cleared'; | |
| 214 | + | |
| 215 | + case 'sitemap': | |
| 216 | + if ( ! class_exists( 'ThinkRank\\SEO\\Sitemap_Generator' ) ) { | |
| 217 | + return 'skipped'; | |
| 218 | + } | |
| 219 | + | |
| 220 | + // Rebuilds rather than invalidates: the sitemap is a static file | |
| 221 | + // on most installs, so there is no next request that would | |
| 222 | + // regenerate it. Lock-guarded, and reports false when another | |
| 223 | + // process holds the lock or the write fails, so the caller is | |
| 224 | + // told the served file did not change. | |
| 225 | + return ( new \ThinkRank\SEO\Sitemap_Generator( false ) ) | |
| 226 | + ->regenerate_sitemap_from_settings() ? 'cleared' : 'failed'; | |
| 198 | 227 | } |
| 199 | 228 | |
| 200 | - return false; | |
| 229 | + return 'skipped'; | |
| 201 | 230 | } |
| 202 | 231 | |
| 203 | 232 | /** |
| 204 | 233 | * Delete ThinkRank's prefixed transients. |