* The cloudflare CDN class.
* @subpackage LiteSpeed/src/cdn
* @author LiteSpeed Technologies <info@litespeedtech.com>
use LiteSpeed\Admin_Display;
defined('WPINC') || exit();
class Cloudflare extends Base {
const TYPE_PURGE_ALL = 'purge_all';
const TYPE_GET_DEVMODE = 'get_devmode';
const TYPE_SET_DEVMODE_ON = 'set_devmode_on';
const TYPE_SET_DEVMODE_OFF = 'set_devmode_off';
const ITEM_STATUS = 'status';
* Update zone&name based on latest settings
public function try_refresh_zone() {
if (!$this->conf(self::O_CDN_CLOUDFLARE)) {
$zone = $this->fetch_zone();
$this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_NAME, $zone['name']);
$this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_ZONE, $zone['id']);
Debug2::debug("[Cloudflare] Get zone successfully \t\t[ID] " . $zone['id']);
$this->cls('Conf')->update(self::O_CDN_CLOUDFLARE_ZONE, '');
Debug2::debug('[Cloudflare] ❌ Get zone failed, clean zone');
* Get Cloudflare development mode
* @param bool $show_msg Whether to show success/error message.
private function get_devmode( $show_msg = true ) {
Debug2::debug('[Cloudflare] get_devmode');
$url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/settings/development_mode';
$res = $this->cloudflare_call($url, 'GET', false, $show_msg);
Debug2::debug('[Cloudflare] get_devmode result ', $res);
// Make sure is array: #992174
$curr_status = self::get_option(self::ITEM_STATUS, array());
if ( ! is_array( $curr_status ) ) {
$curr_status['devmode'] = $res['value'];
$curr_status['devmode_expired'] = (int) $res['time_remaining'] + time();
self::update_option(self::ITEM_STATUS, $curr_status);
* Set Cloudflare development mode
* @param string $type The type of development mode to set (on/off).
private function set_devmode( $type ) {
Debug2::debug('[Cloudflare] set_devmode');
$url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/settings/development_mode';
$new_val = self::TYPE_SET_DEVMODE_ON === $type ? 'on' : 'off';
$data = array( 'value' => $new_val );
$res = $this->cloudflare_call($url, 'PATCH', $data);
$res = $this->get_devmode(false);
$msg = sprintf(__('Notified Cloudflare to set development mode to %s successfully.', 'litespeed-cache'), strtoupper($new_val));
Admin_Display::success($msg);
* Shortcut to purge Cloudflare
* @param string|bool $reason The reason for purging, or false if none.
public static function purge_all( $reason = false ) {
Debug2::debug('[Cloudflare] purge call because: ' . $reason);
self::cls()->purge_all_private();
private function purge_all_private() {
Debug2::debug('[Cloudflare] purge_all_private');
$cf_on = $this->conf(self::O_CDN_CLOUDFLARE);
$msg = __('Cloudflare API is set to off.', 'litespeed-cache');
Admin_Display::error($msg);
$url = 'https://api.cloudflare.com/client/v4/zones/' . $zone . '/purge_cache';
$data = array( 'purge_everything' => true );
$res = $this->cloudflare_call($url, 'DELETE', $data);
$msg = __('Notified Cloudflare to purge all successfully.', 'litespeed-cache');
Admin_Display::success($msg);
* Get current Cloudflare zone from cfg
private function zone() {
$zone = $this->conf(self::O_CDN_CLOUDFLARE_ZONE);
$msg = __('No available Cloudflare zone', 'litespeed-cache');
Admin_Display::error($msg);
* Get Cloudflare zone settings
private function fetch_zone() {
$kw = $this->conf(self::O_CDN_CLOUDFLARE_NAME);
$url = 'https://api.cloudflare.com/client/v4/zones?status=active&match=all';
if ($kw && false !== strpos($kw, '.')) {
$zones = $this->cloudflare_call($url . '&name=' . $kw, 'GET', false, false);
Debug2::debug('[Cloudflare] fetch_zone exact matched');
// Can't find, try to get default one
$zones = $this->cloudflare_call($url, 'GET', false, false);
Debug2::debug('[Cloudflare] fetch_zone no zone');
Debug2::debug('[Cloudflare] fetch_zone no set name, use first one by default');
if (false !== strpos($v['name'], $kw)) {
Debug2::debug('[Cloudflare] fetch_zone matched ' . $kw . ' [name] ' . $v['name']);
// Can't match current name, return default one
Debug2::debug('[Cloudflare] fetch_zone failed match name, use first one by default');
* @param string $url The API URL to call.
* @param string $method The HTTP method to use (GET, POST, etc.).
* @param array|bool $data The data to send with the request, or false if none.
* @param bool $show_msg Whether to show success/error message.
private function cloudflare_call( $url, $method = 'GET', $data = false, $show_msg = true ) {
Debug2::debug("[Cloudflare] cloudflare_call \t\t[URL] $url");
if (strlen($this->conf(self::O_CDN_CLOUDFLARE_KEY)) === 40) {
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->conf(self::O_CDN_CLOUDFLARE_KEY),
'Content-Type' => 'application/json',
'X-Auth-Email' => $this->conf(self::O_CDN_CLOUDFLARE_EMAIL),
'X-Auth-Key' => $this->conf(self::O_CDN_CLOUDFLARE_KEY),
$data = wp_json_encode($data);
$wp_args['body'] = $data;
$resp = wp_remote_request($url, $wp_args);
if (is_wp_error($resp)) {
Debug2::debug('[Cloudflare] error in response');
$msg = __('Failed to communicate with Cloudflare', 'litespeed-cache');
Admin_Display::error($msg);
$result = wp_remote_retrieve_body($resp);
$json = \json_decode($result, true);
if ($json && $json['success'] && $json['result']) {
Debug2::debug('[Cloudflare] cloudflare_call called successfully');
$msg = __('Communicated with Cloudflare successfully.', 'litespeed-cache');
Admin_Display::success($msg);
Debug2::debug("[Cloudflare] cloudflare_call called failed: $result");
$msg = __('Failed to communicate with Cloudflare', 'litespeed-cache');
Admin_Display::error($msg);
* Handle all request actions from main cls
public function handler() {
$type = Router::verify_type();
case self::TYPE_PURGE_ALL:
$this->purge_all_private();
case self::TYPE_GET_DEVMODE:
case self::TYPE_SET_DEVMODE_ON:
case self::TYPE_SET_DEVMODE_OFF:
$this->set_devmode($type);