| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) |
| 3 |
exit; |
| 4 |
|
| 5 |
class BerqCloudflareAPIHandler |
| 6 |
{ |
| 7 |
private $api_url = 'https://api.cloudflare.com/client/v4/'; |
| 8 |
private $api_email; |
| 9 |
private $api_key; |
| 10 |
private $zone_id; |
| 11 |
|
| 12 |
public function __construct($api_email, $api_key, $zone_id) |
| 13 |
{ |
| 14 |
$this->api_email = $api_email; |
| 15 |
$this->api_key = $api_key; |
| 16 |
$this->zone_id = $zone_id; |
| 17 |
} |
| 18 |
|
| 19 |
public function verify_credentials() |
| 20 |
{ |
| 21 |
$endpoint = "zones/{$this->zone_id}"; |
| 22 |
$response = $this->make_request($endpoint, 'GET'); |
| 23 |
|
| 24 |
return isset($response['success']) && $response['success']; |
| 25 |
} |
| 26 |
|
| 27 |
public function add_rule() |
| 28 |
{ |
| 29 |
$rules_rep = $this->get_cache_ruleset(); |
| 30 |
$rules = !empty($rules_rep['result']['rules']) ? $rules_rep['result']['rules'] : false; |
| 31 |
$rule_found = false; |
| 32 |
|
| 33 |
if (!empty($rules)) { |
| 34 |
foreach ($rules as $rule) { |
| 35 |
// Cloudflare stores description and enabled on the rule object, |
| 36 |
// not inside action_parameters. |
| 37 |
if (isset($rule['description']) && $rule['description'] === 'BerqWP cache rules') { |
| 38 |
$rule_found = true; |
| 39 |
break; |
| 40 |
} |
| 41 |
} |
| 42 |
} |
| 43 |
|
| 44 |
if (!$rule_found) { |
| 45 |
$this->update_cache_rules(); |
| 46 |
} |
| 47 |
} |
| 48 |
|
| 49 |
public function get_cache_ruleset() |
| 50 |
{ |
| 51 |
$endpoint = "zones/{$this->zone_id}/rulesets/phases/http_request_cache_settings/entrypoint"; |
| 52 |
return $this->make_request($endpoint, 'GET'); |
| 53 |
} |
| 54 |
|
| 55 |
public function delete_rule_by_description($description) { |
| 56 |
// Step 1: Get the ruleset from Cloudflare |
| 57 |
$response = $this->get_cache_ruleset(); |
| 58 |
|
| 59 |
// Handle API errors |
| 60 |
if (!isset($response['success']) || !$response['success']) { |
| 61 |
return [ |
| 62 |
'success' => false, |
| 63 |
'message' => 'Failed to fetch ruleset: ' . ($response['errors'][0]['message'] ?? 'Unknown error'), |
| 64 |
]; |
| 65 |
} |
| 66 |
|
| 67 |
// Extract the ruleset and its rules |
| 68 |
$ruleset = $response['result'] ?? []; |
| 69 |
$rules = $ruleset['rules'] ?? []; |
| 70 |
|
| 71 |
// Step 2: Find and remove the rule by description |
| 72 |
$updatedRules = []; |
| 73 |
$found = false; |
| 74 |
|
| 75 |
foreach ($rules as $rule) { |
| 76 |
if (isset($rule['description']) && $rule['description'] === $description) { |
| 77 |
$found = true; |
| 78 |
} else { |
| 79 |
$updatedRules[] = $rule; // Keep rules that don't match |
| 80 |
} |
| 81 |
} |
| 82 |
|
| 83 |
if (!$found) { |
| 84 |
return [ |
| 85 |
'success' => false, |
| 86 |
'message' => "No rule found with description: {$description}", |
| 87 |
]; |
| 88 |
} |
| 89 |
|
| 90 |
// Step 3: Send only the rules array back to Cloudflare. |
| 91 |
// Sending read-only fields (id, version, kind, phase) is non-idiomatic and fragile. |
| 92 |
$update_endpoint = "zones/{$this->zone_id}/rulesets/{$ruleset['id']}"; |
| 93 |
$update_response = $this->make_request($update_endpoint, 'PUT', ['rules' => $updatedRules]); |
| 94 |
|
| 95 |
// Step 5: Return result |
| 96 |
if (isset($update_response['success']) && $update_response['success']) { |
| 97 |
return [ |
| 98 |
'success' => true, |
| 99 |
'message' => 'Rule deleted successfully.', |
| 100 |
]; |
| 101 |
} else { |
| 102 |
return [ |
| 103 |
'success' => false, |
| 104 |
'message' => 'Failed to update ruleset: ' . |
| 105 |
implode(' ', array_column($update_response['errors'] ?? [], 'message')), |
| 106 |
]; |
| 107 |
} |
| 108 |
} |
| 109 |
|
| 110 |
public function purge_all_cache() { |
| 111 |
$endpoint = "zones/{$this->zone_id}/purge_cache"; |
| 112 |
$response = $this->make_request($endpoint, 'POST', [ |
| 113 |
'purge_everything' => true |
| 114 |
]); |
| 115 |
|
| 116 |
if (is_array($response) && isset($response['success']) && $response['success']) { |
| 117 |
return [ |
| 118 |
'success' => true, |
| 119 |
'message' => 'All cache purged successfully.', |
| 120 |
]; |
| 121 |
} |
| 122 |
|
| 123 |
$errors = is_array($response) && isset($response['errors']) |
| 124 |
? implode(' ', array_column($response['errors'], 'message')) |
| 125 |
: 'Unknown error'; |
| 126 |
|
| 127 |
return [ |
| 128 |
'success' => false, |
| 129 |
'message' => 'Failed to purge cache: ' . $errors, |
| 130 |
]; |
| 131 |
} |
| 132 |
|
| 133 |
// Method to flush a specific URL from cache |
| 134 |
public function flush_url($url) { |
| 135 |
$endpoint = "zones/{$this->zone_id}/purge_cache"; |
| 136 |
$response = $this->make_request($endpoint, 'POST', [ |
| 137 |
'files' => [$url] |
| 138 |
]); |
| 139 |
|
| 140 |
if (is_array($response) && isset($response['success']) && $response['success']) { |
| 141 |
return [ |
| 142 |
'success' => true, |
| 143 |
'message' => "Cache for {$url} purged successfully." |
| 144 |
]; |
| 145 |
} |
| 146 |
|
| 147 |
$errors = is_array($response) && isset($response['errors']) |
| 148 |
? implode(' ', array_column($response['errors'], 'message')) |
| 149 |
: 'Unknown error'; |
| 150 |
|
| 151 |
return [ |
| 152 |
'success' => false, |
| 153 |
'message' => 'Failed to purge URL: ' . $errors, |
| 154 |
]; |
| 155 |
} |
| 156 |
|
| 157 |
public function update_cache_rules() |
| 158 |
{ |
| 159 |
$endpoint = "zones/{$this->zone_id}/rulesets/phases/http_request_cache_settings/entrypoint"; |
| 160 |
|
| 161 |
// GET existing rules so we don't wipe other CF cache rules in the zone. |
| 162 |
$existing = $this->get_cache_ruleset(); |
| 163 |
$existing_rules = !empty($existing['result']['rules']) ? $existing['result']['rules'] : []; |
| 164 |
|
| 165 |
// Remove any old BerqWP rule, keep everything else. |
| 166 |
$merged = []; |
| 167 |
foreach ($existing_rules as $rule) { |
| 168 |
if (!isset($rule['description']) || $rule['description'] !== 'BerqWP cache rules') { |
| 169 |
$merged[] = $rule; |
| 170 |
} |
| 171 |
} |
| 172 |
|
| 173 |
// Append the canonical BerqWP cache rule. |
| 174 |
$merged[] = [ |
| 175 |
'expression' => 'not (http.request.method ne "GET" or http.cookie contains "wordpress_logged_in_" or http.request.uri.path contains "/wp-admin" or http.request.uri.path contains ".xml" or http.request.uri.path contains ".txt" or http.request.uri.path contains ".gz" or http.request.uri.path contains "sitemap" or http.request.uri.query ne "")', |
| 176 |
'action' => 'set_cache_settings', |
| 177 |
'action_parameters' => [ |
| 178 |
'cache' => true, |
| 179 |
], |
| 180 |
'description' => 'BerqWP cache rules', |
| 181 |
]; |
| 182 |
|
| 183 |
$response = $this->make_request($endpoint, 'PUT', ['rules' => $merged]); |
| 184 |
|
| 185 |
if (is_array($response) && isset($response['success']) && $response['success']) { |
| 186 |
return [ |
| 187 |
'success' => true, |
| 188 |
'message' => 'Cache rules updated successfully' |
| 189 |
]; |
| 190 |
} |
| 191 |
|
| 192 |
return [ |
| 193 |
'success' => false, |
| 194 |
'message' => is_array($response) && isset($response['errors']) ? json_encode($response['errors']) : 'Unknown error occurred' |
| 195 |
]; |
| 196 |
} |
| 197 |
|
| 198 |
private function make_request($endpoint, $method = 'GET', $data = null) |
| 199 |
{ |
| 200 |
$url = $this->api_url . $endpoint; |
| 201 |
|
| 202 |
$args = [ |
| 203 |
'method' => $method, |
| 204 |
'headers' => [ |
| 205 |
'Content-Type' => 'application/json', |
| 206 |
'X-Auth-Email' => $this->api_email, |
| 207 |
'X-Auth-Key' => $this->api_key |
| 208 |
], |
| 209 |
'timeout' => 30 |
| 210 |
]; |
| 211 |
|
| 212 |
if ($data !== null) { |
| 213 |
$args['body'] = json_encode($data); |
| 214 |
} |
| 215 |
|
| 216 |
$response = wp_remote_request($url, $args); |
| 217 |
|
| 218 |
if (is_wp_error($response)) { |
| 219 |
return [ |
| 220 |
'success' => false, |
| 221 |
'errors' => [['message' => $response->get_error_message()]] |
| 222 |
]; |
| 223 |
} |
| 224 |
|
| 225 |
$body = wp_remote_retrieve_body($response); |
| 226 |
return json_decode($body, true); |
| 227 |
} |
| 228 |
} |