Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0 refactor #1

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
# ZuidWest Cache Manager for Cloudflare
... Work in Progress
# ZuidWest Cache Manager

The ZuidWest Cache Manager is a WordPress plugin for high-traffic sites on non-enterprise Cloudflare accounts. It instantly clears cache for articles and homepage URLs when posts are updated or published, and batches related taxonomy URLs for low-priority processing with WP-Cron. This setup allows for extended caching times with Cloudflare's 'cache everything' feature, significantly lowering traffic to the origin server.

## Features

- **Immediate Cache Purging**: Automatically purges cache for high-priority URLs (e.g., the post URL and homepage) immediately upon post publishing or editing.
- **Low-Priority Batch Processing**: Queues associated taxonomy URLs and processes them in batches for efficient cache management, reducing server load.
- **Customizable Settings**: Offers a settings page in the WordPress admin area for configuring API keys, batch sizes, and debug mode.
- **Debug Mode**: Includes an optional debug mode for logging operations and troubleshooting.

## Configuration

Navigate to the plugin settings page located under Settings > ZuidWest Cache in the WordPress admin dashboard. Enter the following details:

- **Zone ID**: Your Cloudflare Zone ID.
- **API Key**: Your Cloudflare API Key.
- **Batch Size**: The number of URLs to process per batch during low-priority batch processing.
- **Debug Mode**: Enable or disable debug mode for logging.

After entering correct credentials the plug-in listens for post publishing and editing events to purge or queue URLs accordingly.

## Debugging

Enable debug mode in the plugin settings to log detailed information about cache purging operations, which can be helpful for troubleshooting.

## Contributing

Contributions are welcome. Please feel free to submit pull requests or report issues on our GitHub repository.
1 change: 1 addition & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<arg value="nps"/>

<file>zuidwest-cache-manager.php</file>
<file>src/</file>

<!-- Fix crash on PHP 8 -->
<ini name="error_reporting" value="E_ALL &#38; ~E_DEPRECATED"/>
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ parameters:
level: 5
paths:
- zuidwest-cache-manager.php
- src/
66 changes: 66 additions & 0 deletions src/admin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

// Registers a new item in the WordPress admin menu for the ZuidWest Cache Manager settings page.
function zwcache_add_admin_menu()
{
add_options_page('ZuidWest Cache Manager Settings', 'ZuidWest Cache', 'manage_options', 'zwcache_manager', 'zwcache_options_page');
}

// Initializes settings for the ZuidWest Cache Manager plugin.
function zwcache_settings_init()
{
register_setting('zwcachePlugin', 'zwcache_settings');
add_settings_section('zwcache_pluginPage_section', 'API Settings', 'zwcache_settings_section_callback', 'zwcachePlugin');

$fields = [
['zwcache_zone_id', 'Zone ID', 'text'],
['zwcache_api_key', 'API Key', 'text'],
['zwcache_batch_size', 'Batch Size', 'number', 30],
['zwcache_debug_mode', 'Debug Mode', 'checkbox', 1]
];

foreach ($fields as $field) {
add_settings_field($field[0], $field[1], 'zwcache_render_settings_field', 'zwcachePlugin', 'zwcache_pluginPage_section', ['label_for' => $field[0], 'type' => $field[2], 'default' => $field[3] ?? '']);
}
}

/**
* Renders a settings field.
*
* @param array $args Arguments containing the settings field configuration.
*/
function zwcache_render_settings_field($args)
{
$options = get_option('zwcache_settings');
$value = $options[$args['label_for']] ?? $args['default'];
$checked = ($value) ? 'checked' : '';

echo match ($args['type']) {
'text', 'number' => "<input type='" . $args['type'] . "' id='" . $args['label_for'] . "' name='zwcache_settings[" . $args['label_for'] . "]' value='" . $value . "'>",
'checkbox' => "<input type='checkbox' id='" . $args['label_for'] . "' name='zwcache_settings[" . $args['label_for'] . "]' value='1' " . $checked . '>',
default => ''
};
}

// Callback function for the settings section.
function zwcache_settings_section_callback()
{
echo 'Enter your settings below:';
}

// Renders the ZuidWest Cache Manager settings page.
function zwcache_options_page()
{
?>
<div class="wrap">
<h2>ZuidWest Cache Manager Settings</h2>
<form action="options.php" method="post">
<?php
settings_fields('zwcachePlugin');
do_settings_sections('zwcachePlugin');
submit_button();
?>
</form>
</div>
<?php
}
20 changes: 20 additions & 0 deletions src/cron.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Adds custom cron schedule if it doesn't already exist.
*
* @param array $schedules The existing schedules.
* @return array The modified schedules.
*/
function zwcache_add_cron_interval($schedules)
{
if (!isset($schedules['every_minute'])) {
$schedules['every_minute'] = [
'interval' => 60,
'display' => esc_html__('Every minute'),
];
}
return $schedules;
}

add_filter('cron_schedules', 'zwcache_add_cron_interval');
25 changes: 25 additions & 0 deletions src/hooks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

/**
* Activates the plugin and schedules a WP-Cron event if not already scheduled.
*/
function zwcache_activate()
{
if (!wp_next_scheduled(ZWCACHE_CRON_HOOK)) {
wp_schedule_event(time(), 'every_minute', ZWCACHE_CRON_HOOK);
}
zwcache_debug_log('Plugin activated and cron job scheduled.');
}

/**
* Deactivates the plugin, clears the scheduled WP-Cron event, and deletes the option storing low-priority URLs.
*/
function zwcache_deactivate()
{
wp_clear_scheduled_hook(ZWCACHE_CRON_HOOK);
delete_option(ZWCACHE_LOW_PRIORITY_STORE);
zwcache_debug_log('Plugin deactivated and cron job cleared.');
}

register_activation_hook(__FILE__, 'zwcache_activate');
register_deactivation_hook(__FILE__, 'zwcache_deactivate');
45 changes: 45 additions & 0 deletions src/post.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* Handles the transition of a post's status and purges or queues URLs for purging as necessary.
*
* @param string $new_status New status of the post.
* @param string $old_status Old status of the post.
* @param WP_Post $post Post object.
*/
function zwcache_handle_post_status_change($new_status, $old_status, $post)
{
zwcache_debug_log('Post status changed from ' . $old_status . ' to ' . $new_status . ' for post ID ' . $post->ID . '.');
if ($new_status === 'publish' || $old_status === 'publish') {
zwcache_purge_urls([get_permalink($post->ID), get_home_url()]);
zwcache_queue_low_priority_urls(zwcache_get_associated_taxonomy_urls($post->ID));
}
}

add_action('transition_post_status', 'zwcache_handle_post_status_change', 10, 3);

/**
* Retrieves URLs for all taxonomies associated with a given post.
*
* @param int $post_id ID of the post.
* @return array URLs associated with the post's taxonomies.
*/
function zwcache_get_associated_taxonomy_urls($post_id)
{
zwcache_debug_log('Retrieving associated taxonomy URLs for post ID ' . $post_id . '.');
$urls = [];
$taxonomies = get_post_taxonomies($post_id);
foreach ($taxonomies as $taxonomy) {
$terms = get_the_terms($post_id, $taxonomy);
if ($terms && !is_wp_error($terms)) {
foreach ($terms as $term) {
$term_link = get_term_link($term, $taxonomy);
if (!is_wp_error($term_link)) {
$urls[] = $term_link;
}
}
}
}
zwcache_debug_log(sprintf('Found %d associated taxonomy URLs.', count($urls)));
return $urls;
}
78 changes: 78 additions & 0 deletions src/purge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

/**
* Queues URLs for low-priority batch processing.
*
* @param array $urls URLs to be queued.
*/
function zwcache_queue_low_priority_urls($urls)
{
$queued_urls = get_option(ZWCACHE_LOW_PRIORITY_STORE, []);
$new_urls = array_filter($urls, function ($url) use ($queued_urls) {
return !in_array($url, $queued_urls);
});

if (!empty($new_urls)) {
update_option(ZWCACHE_LOW_PRIORITY_STORE, array_merge($queued_urls, $new_urls));
zwcache_debug_log('Queued new URLs for low-priority purging.');
}
}

/**
* Processes queued URLs in batches for low-priority purging.
*/
function zwcache_process_queued_low_priority_urls()
{
zwcache_debug_log('Processing queued low-priority URLs.');
$queued_urls = get_option(ZWCACHE_LOW_PRIORITY_STORE, []);
if (empty($queued_urls)) {
zwcache_debug_log('No URLs in queue to process.');
return;
}
$batch_size = zwcache_get_option('zwcache_batch_size', 30);
$total_urls = count($queued_urls);
for ($i = 0; $i < $total_urls; $i += $batch_size) {
$batch = array_slice($queued_urls, $i, $batch_size);
if (zwcache_purge_urls($batch)) {
$queued_urls = array_diff($queued_urls, $batch);
zwcache_debug_log('Removed processed URLs from batch');
}
}
update_option(ZWCACHE_LOW_PRIORITY_STORE, $queued_urls);
}

/**
* Purges the specified URLs through the Cloudflare API.
*
* @param array $urls URLs to be purged.
* @return bool True if purge was successful, false otherwise.
*/
function zwcache_purge_urls($urls)
{
zwcache_debug_log('Attempting to purge URLs: ' . implode(', ', $urls));
$zone_id = zwcache_get_option('zwcache_zone_id');
$api_key = zwcache_get_option('zwcache_api_key');

// Check if either the zone ID or API key is empty
if (empty($zone_id) || empty($api_key)) {
zwcache_debug_log('Zone ID or API Key is not set. Aborting cache purge.');
return false; // Exit the function if either is not set
}

$apiEndpoint = 'https://api.cloudflare.com/client/v4/zones/' . $zone_id . '/purge_cache';
$response = wp_remote_post($apiEndpoint, [
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
],
'body' => json_encode(['files' => $urls]),
]);

if (is_wp_error($response) || wp_remote_retrieve_response_code($response) != 200) {
zwcache_debug_log('Cache purge failed: ' . wp_remote_retrieve_body($response));
return false;
} else {
zwcache_debug_log('Successfully purged URLs.');
return true;
}
}
26 changes: 26 additions & 0 deletions src/utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* Retrieves a specific option value from the ZuidWest cache plugin settings.
*
* @param string $option_name The name of the option to retrieve.
* @param mixed $default Optional. The default value to return if the option does not exist. Default is false.
* @return mixed The value of the specified option, or the default value if the option is not found.
*/
function zwcache_get_option($option_name, $default = false)
{
$options = get_option('zwcache_settings');
return $options[$option_name] ?? $default;
}

/**
* Logs a debug message to the PHP error log if debug mode is enabled.
*
* @param string $message The debug message to log.
*/
function zwcache_debug_log($message)
{
if (zwcache_get_option('zwcache_debug_mode', true)) {
error_log('ZuidWest Cache Manager: ' . $message);
}
}
Loading