From 807e74660894e9bb968b7b78b5c8e0e949a31a9f Mon Sep 17 00:00:00 2001 From: datorik Date: Mon, 3 Aug 2026 14:32:46 +0300 Subject: [PATCH 1/8] New.Code.Init constants. --- cleantalk.php | 28 ++- inc/cleantalk-common.php | 4 +- inc/cleantalk-pluggable.php | 6 +- inc/cleantalk-public.php | 12 +- inc/cleantalk-settings.php | 17 +- .../CleantalkPreprocessComment.php | 14 +- .../IntegrationsByClass/BuddyPress.php | 4 +- lib/Cleantalk/ApbctWP/Activator.php | 2 +- lib/Cleantalk/ApbctWP/AdminNotices.php | 2 +- lib/Cleantalk/ApbctWP/ApbctConstant.php | 42 +++- .../ApbctWP/Firewall/AntiCrawler.php | 2 +- .../Localize/CtPublicFunctionsLocalize.php | 1 + lib/Cleantalk/ApbctWP/RemoteCalls.php | 2 +- lib/Cleantalk/ApbctWP/ServiceConstants.php | 183 +++++++++++++----- lib/Cleantalk/ApbctWP/State.php | 5 +- tests/ApbctWP/TestServiceConstants.php | 34 +++- 16 files changed, 262 insertions(+), 96 deletions(-) diff --git a/cleantalk.php b/cleantalk.php index c1eb6c4bb..c0aa328f2 100644 --- a/cleantalk.php +++ b/cleantalk.php @@ -56,7 +56,18 @@ die('Not allowed!'); } -global $apbct, $wpdb, $pagenow; +/** + * @var State $apbct + */ +global $apbct; +/** + * @var \wpdb $wpdb + */ +global $wpdb; +/** + * @var mixed|string $pagenow + */ +global $pagenow; $cleantalk_executed = false; @@ -129,6 +140,7 @@ } define('APBCT_AGENT', 'wordpress-' . $plugin_version__agent); // Prepared agent +//todo make this as $apbct->service_constants if ( defined('CLEANTALK_SERVER') ) { define('APBCT_MODERATE_URL', 'https://moderate.' . CLEANTALK_SERVER); if ( ! defined('CLEANTALK_API_URL') ) { @@ -263,7 +275,7 @@ function apbct_register_my_rest_routes() // Database prefix global $wpdb, $wp_version; $apbct->db_prefix = ! APBCT_WPMS || $apbct->allow_custom_key || $apbct->white_label ? $wpdb->prefix : $wpdb->base_prefix; -$apbct->db_prefix = ! $apbct->white_label && defined('CLEANTALK_ACCESS_KEY') ? $wpdb->base_prefix : $wpdb->prefix; +$apbct->db_prefix = ! $apbct->white_label && $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ? $wpdb->base_prefix : $wpdb->prefix; /** @todo HARDCODE FIX */ if ( $apbct->plugin_version === '1.0.0' ) { @@ -1253,8 +1265,8 @@ function apbct_sfw_update__switch_to_direct() $apbct->fw_stats['reason_direct_update_log'] = null; - if (defined('APBCT_SFW_FORCE_DIRECT_UPDATE')) { - $apbct->fw_stats['reason_direct_update_log'] = 'const APBCT_SFW_FORCE_DIRECT_UPDATE exists'; + if ($apbct->constants->sfw_force_direct_update->isDefined()) { + $apbct->fw_stats['reason_direct_update_log'] = 'constant exists'; return true; } @@ -2808,15 +2820,15 @@ function ct_account_status_check($api_key = null, $process_errors = true) : 0; //todo:temporary solution for description, until we found the way to transfer this from cloud - if (defined('APBCT_WHITELABEL_PLUGIN_DESCRIPTION')) { + if ($apbct->constants->whitelabel_plugin_description->isDefinedAndTypeOK()) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_antispam_description'] = APBCT_WHITELABEL_PLUGIN_DESCRIPTION; + $result['wl_antispam_description'] = esc_html($apbct->constants->whitelabel_plugin_description->getValue()); } //todo:temporary solution for FAQ - if (defined('APBCT_WHITELABEL_FAQ_LINK')) { + if ($apbct->constants->whitelabel_faq_link->isDefinedAndTypeOK()) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_faq_url'] = APBCT_WHITELABEL_FAQ_LINK; + $result['wl_faq_url'] = esc_url($apbct->constants->whitelabel_faq_link->getValue()); } if ( isset($result['wl_status']) && $result['wl_status'] === 'ON' ) { diff --git a/inc/cleantalk-common.php b/inc/cleantalk-common.php index 54cf6d4ea..afab7c29f 100644 --- a/inc/cleantalk-common.php +++ b/inc/cleantalk-common.php @@ -218,7 +218,7 @@ function apbct_base_call($params = array(), $reg_flag = false) * @since 6.58.99 */ if ( - $apbct->service_constants->disable_empty_email_exception->isDefined() && + $apbct->constants->disable_empty_email_exception->isDefined() && !$apbct->settings['data__general_postdata_test'] ) { $params['exception_action'] = 0; @@ -1858,7 +1858,7 @@ function apbct__bot_detector_get_fd_log() ); // Initialize result array with default values - if (defined('APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS')) { + if ($apbct->constants->do_not_collect_frontend_data_logs->isDefined()) { $result['plugin_status'] = 'OK'; $result['error_msg'] = 'bot detector logs collection is disabled via constant definition'; return json_encode($result); diff --git a/inc/cleantalk-pluggable.php b/inc/cleantalk-pluggable.php index d6c11b6b5..5bfa7e8eb 100644 --- a/inc/cleantalk-pluggable.php +++ b/inc/cleantalk-pluggable.php @@ -2129,10 +2129,10 @@ function apbct_settings__get_ajax_type() global $apbct; //force ajax route type if constant is defined and compatible - if ($apbct->service_constants->set_ajax_route_type->isDefined() - && in_array($apbct->service_constants->set_ajax_route_type->getValue(), array('rest','admin_ajax')) + if ($apbct->constants->set_ajax_route_type->isDefinedAndTypeOK() + && in_array($apbct->constants->set_ajax_route_type->getValue(), array('rest','admin_ajax')) ) { - return $apbct->service_constants->set_ajax_route_type->getValue(); + return $apbct->constants->set_ajax_route_type->getValue(); } // Check rest availability diff --git a/inc/cleantalk-public.php b/inc/cleantalk-public.php index 007f6983e..57584e837 100644 --- a/inc/cleantalk-public.php +++ b/inc/cleantalk-public.php @@ -41,7 +41,7 @@ function apbct_init() // Localize data if ( ! apbct_exclusions_check__url() ) { - if (defined('CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER') && CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER) { + if ($apbct->constants->place_public_js_scripts_in_footer->isDefined()) { add_action('wp_footer', array(LocalizeHandler::class, 'handle'), 1); add_action('login_footer', array(LocalizeHandler::class, 'handle'), 1); } else { @@ -811,7 +811,7 @@ function apbct_comment__wordpress__show_blacklists($notify_message, $comment_id) */ function ct_die($_comment_id, $_comment_status) { - global $ct_comment, $ct_jp_comments; + global $ct_comment, $ct_jp_comments, $apbct; // JCH Optimize caching preventing add_filter('jch_optimize_page_cache_set_caching', static function ($_is_cache_active) { @@ -821,7 +821,7 @@ function ct_die($_comment_id, $_comment_status) do_action('apbct_pre_block_page', $ct_comment); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( defined('CLEANTALK_DISABLE_BLOCKING_TITLE') && CLEANTALK_DISABLE_BLOCKING_TITLE != true ) { + if ( ! $apbct->constants->disable_blocking_title->isDefined() ) { $message_title = 'CleanTalk. ' . $message_title; } if ( Post::get('et_pb_contact_email') ) { @@ -870,7 +870,7 @@ function ct_die($_comment_id, $_comment_status) */ function ct_die_extended($comment_body) { - global $ct_jp_comments; + global $ct_jp_comments, $apbct; // JCH Optimize caching preventing add_filter('jch_optimize_page_cache_set_caching', static function ($_is_cache_active) { @@ -878,7 +878,7 @@ function ct_die_extended($comment_body) }, 999, 1); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( defined('CLEANTALK_DISABLE_BLOCKING_TITLE') && CLEANTALK_DISABLE_BLOCKING_TITLE != true ) { + if ( !$apbct->constants->disable_blocking_title->isDefined() ) { $message_title = 'CleanTalk. ' . $message_title; } @@ -1263,7 +1263,7 @@ function apbct_enqueue_and_localize_public_scripts() { global $apbct; - $in_footer = defined('CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER') && CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER; + $in_footer = $apbct->constants->place_public_js_scripts_in_footer->isDefined(); // Different JS params $bundle_name = ApbctJsBundleResolver::getBundleName($apbct->settings); ApbctEnqueue::getInstance()->js($bundle_name, array(), $in_footer); diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php index 8aacc9a3a..79b1fa36c 100644 --- a/inc/cleantalk-settings.php +++ b/inc/cleantalk-settings.php @@ -492,7 +492,9 @@ function apbct_settings__set_fields() 'title' => __("Don't check trusted user's comments", 'cleantalk-spam-protect'), 'description' => sprintf( __("Don't check comments for users with above %d comments.", 'cleantalk-spam-protect'), - defined('CLEANTALK_CHECK_COMMENTS_NUMBER') ? CLEANTALK_CHECK_COMMENTS_NUMBER : 3 + $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() + ? $apbct->constants->skip_on_approved_comments_number->getValue() + : 3 ), ), 'data__use_ajax' => array( @@ -1148,7 +1150,7 @@ function apbct_settings__set_fields__network($fields) '' ), 'childrens' => array('multisite__white_label__plugin_name'), - 'disabled' => defined('CLEANTALK_ACCESS_KEY') || + 'disabled' => $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() || ! isset($apbct->network_settings['multisite__work_mode']) || $apbct->network_settings['multisite__work_mode'] != 1, 'parent' => 'multisite__work_mode', @@ -1757,7 +1759,7 @@ function apbct_settings__error__output($return = false) $out = ''; - if ( ! empty($apbct->errors) && ! defined('CLEANTALK_ACCESS_KEY') ) { + if ( ! empty($apbct->errors) && ! $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ) { $errors = $apbct->errors; $error_texts = array( @@ -2051,7 +2053,10 @@ function apbct_settings__field__apikey() $template = @file_get_contents(CLEANTALK_PLUGIN_DIR . 'templates/settings/settings_key_wrapper.html'); - $define_key_is_provided_by_admin = APBCT_WPMS && ! is_main_site() && ( ! $apbct->allow_custom_key || defined('CLEANTALK_ACCESS_KEY')); + $define_key_is_provided_by_admin = APBCT_WPMS && ! is_main_site() && ( + ! $apbct->allow_custom_key || + $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() + ); $define_show_key_field = ! (apbct_api_key__is_correct($apbct->api_key) && isset($apbct->data["key_changed"]) && $apbct->data["key_changed"]); $define_show_deobfuscating_href = apbct_api_key__is_correct($apbct->api_key) && $apbct->key_is_ok && (!isset($apbct->data["key_changed"]) || !$apbct->data["key_changed"]); @@ -2424,6 +2429,10 @@ function apbct_settings__validate($incoming_settings) $apbct->data['key_changed'] = $incoming_settings['apikey'] !== $apbct->settings['apikey']; + $predefined_key = $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() + ? $apbct->constants->self_owned_access_key->getValue() + : false; + $incoming_settings['apikey'] = ! empty($incoming_settings['apikey']) ? trim($incoming_settings['apikey']) : ''; $incoming_settings['apikey'] = defined('CLEANTALK_ACCESS_KEY') ? CLEANTALK_ACCESS_KEY : $incoming_settings['apikey']; $incoming_settings['apikey'] = ! is_main_site() && $apbct->white_label && $apbct->settings['apikey'] ? $apbct->settings['apikey'] : $incoming_settings['apikey']; diff --git a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php index 0f0f9699a..14be09f45 100644 --- a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php +++ b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php @@ -64,7 +64,9 @@ public function doPrepareActions($argument) $this->wp_comment_post_id = $comment_post_id; $this->post_info = array(); - $this->comments_check_number_needs_to_skip_request = defined('CLEANTALK_CHECK_COMMENTS_NUMBER') ? CLEANTALK_CHECK_COMMENTS_NUMBER : 3; + $this->comments_check_number_needs_to_skip_request = $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() + ? $apbct->constants->skip_on_approved_comments_number->getValue() + : 3; /** * Custom mail notifications processing @@ -315,7 +317,7 @@ public function doBlock($message) { $ct_result = $this->base_call_result['ct_result']; - global $ct_comment, $ct_stop_words; + global $ct_comment, $ct_stop_words, $apbct; $ct_comment = $message; $ct_stop_words = $ct_result->stop_words; /** @@ -329,9 +331,11 @@ public function doBlock($message) $err_text = '
' - . ((defined('CLEANTALK_DISABLE_BLOCKING_TITLE') && CLEANTALK_DISABLE_BLOCKING_TITLE == true) - ? '' - : 'CleanTalk. ') + . ( + $apbct->constants->disable_blocking_title->isDefined() + ? '' + : 'CleanTalk. ' + ) . __('Spam protection', 'cleantalk-spam-protect') . "


\n" . $ct_result->comment; diff --git a/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php b/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php index 9bb153ae3..04821cc62 100755 --- a/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php +++ b/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php @@ -78,7 +78,9 @@ public function privateMsgCheck($bp_message_obj) } // Check for quantity of comments - $comments_check_number = defined('CLEANTALK_CHECK_COMMENTS_NUMBER') ? CLEANTALK_CHECK_COMMENTS_NUMBER : 3; + $comments_check_number = $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() + ? $apbct->constants->skip_on_approved_comments_number->getValue() + : 3; if ( $apbct->settings['comments__check_comments_number'] ) { $args = array( diff --git a/lib/Cleantalk/ApbctWP/Activator.php b/lib/Cleantalk/ApbctWP/Activator.php index d546fc4ec..0e88e4b1c 100644 --- a/lib/Cleantalk/ApbctWP/Activator.php +++ b/lib/Cleantalk/ApbctWP/Activator.php @@ -26,7 +26,7 @@ public static function activation($network_wide, $concrete_blog_id = null) $apbct->stats['plugin']['activation__times'] += 1; $apbct->save('stats'); - if ( $network_wide && ! defined('CLEANTALK_ACCESS_KEY') ) { + if ( $network_wide && ! $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ) { $initial_blog = get_current_blog_id(); $blogs = array_keys($wpdb->get_results('SELECT blog_id FROM ' . $wpdb->blogs, OBJECT_K)); foreach ( $blogs as $blog ) { diff --git a/lib/Cleantalk/ApbctWP/AdminNotices.php b/lib/Cleantalk/ApbctWP/AdminNotices.php index 536deb799..7eee425d8 100644 --- a/lib/Cleantalk/ApbctWP/AdminNotices.php +++ b/lib/Cleantalk/ApbctWP/AdminNotices.php @@ -72,7 +72,7 @@ private function __construct() in_array(Get::get('page'), array('cleantalk', 'ct_check_spam', 'ct_check_users')); $this->user_token = $this->apbct->user_token ?: ''; - $self_owned_key = $this->apbct->moderate_ip == 0 && ! defined('CLEANTALK_ACCESS_KEY'); + $self_owned_key = $this->apbct->moderate_ip == 0 && !$this->apbct->constants->self_owned_access_key->isDefinedAndTypeOK(); $is_dashboard = is_network_admin() || is_admin(); $is_admin = current_user_can('activate_plugins'); $uid = get_current_user_id(); diff --git a/lib/Cleantalk/ApbctWP/ApbctConstant.php b/lib/Cleantalk/ApbctWP/ApbctConstant.php index 10cd8ac28..d34787cf9 100644 --- a/lib/Cleantalk/ApbctWP/ApbctConstant.php +++ b/lib/Cleantalk/ApbctWP/ApbctConstant.php @@ -18,17 +18,23 @@ class ApbctConstant */ public $description; - public function __construct(array $allowed_public_names, $description = '') + private $type; + + private $defined_name = false; + + public function __construct(array $allowed_public_names, $type, $description = '') { $this->allowed_public_names = $allowed_public_names; $this->description = $description; + $this->type = $type; + $this->defined_name = $this->getDefinedName(); } /** * If defined, returns the name of the first defined constant from the allowed names. Return false if none of the constants are defined. - * @return false|string + * @return string|false */ - public function isDefined() + private function getDefinedName() { foreach ($this->allowed_public_names as $name) { if (defined($name)) { @@ -38,17 +44,35 @@ public function isDefined() return false; } + /** + * If defined and type is correct. + * @return bool + */ + public function isDefinedAndTypeOK() + { + return $this->defined_name && gettype(constant($this->defined_name)) === $this->type; + } + + + /** + * Return the fact of definition + * @return bool + * @psalm-suppress PossiblyUnusedMethod + */ + public function isDefined() + { + return (bool)$this->defined_name; + } + /** * Returns the value of the first defined constant from the allowed names. Return null if none of the constants are defined. * - * @return string|null + * @return mixed|null */ public function getValue() { - foreach ($this->allowed_public_names as $name) { - if (defined($name)) { - return (string)constant($name); - } + if ($this->defined_name) { + return constant($this->defined_name); } return null; } @@ -59,7 +83,7 @@ public function getValue() public function getData() { return array( - 'is_defined' => $this->isDefined(), + 'is_defined' => $this->defined_name, 'value' => $this->getValue(), 'description' => $this->description, ); diff --git a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php index 7c316b841..157a00908 100644 --- a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php +++ b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php @@ -709,7 +709,7 @@ private function checkExclusions() } // skip for RSS Feed requests - if ($this->apbct->service_constants->skip_anticrawler_on_rss_feed->isDefined()) { + if ($apbct->constants->skip_anticrawler_on_rss_feed->isDefined()) { if (Server::getString('REQUEST_URI') && preg_match_all('/feed/i', Server::getString('REQUEST_URI')) ) { diff --git a/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php b/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php index c1bbcdcaf..9669a106d 100644 --- a/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php +++ b/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php @@ -24,6 +24,7 @@ public static function getData() 'data__ajax_type' => $apbct->data['ajax_type'], 'bot_detector_enabled' => apbct__is_bot_detector_enabled(), 'data__frontend_data_log_enabled' => defined('APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS') ? 0 : 1, + 'data__bot_detector_enabled' => $apbct->settings['data__bot_detector_enabled'], 'cookiePrefix' => apbct__get_cookie_prefix(), 'wprocket_detected' => apbct_is_plugin_active('wp-rocket/wp-rocket.php'), 'host_url' => Server::get('HTTP_HOST'), diff --git a/lib/Cleantalk/ApbctWP/RemoteCalls.php b/lib/Cleantalk/ApbctWP/RemoteCalls.php index 2ebd69466..b4b70ef23 100644 --- a/lib/Cleantalk/ApbctWP/RemoteCalls.php +++ b/lib/Cleantalk/ApbctWP/RemoteCalls.php @@ -375,7 +375,7 @@ public static function action__debug() // phpcs:ignore PSR1.Methods.CamelCapsMet if ($apbct->settings['data__set_cookies'] == 3 && $apbct->data['cookies_type'] === 'alternative') { $out['alt_sessions_auto_state_reason'] = $apbct->isAltSessionsRequired(true); } - $out['active_service_constants'] = $apbct->service_constants->getDefinitionsActive(); + $out['active_service_constants'] = $apbct->constants->getDefinitionsActive(); if ( APBCT_WPMS ) { $out['network_settings'] = $apbct->network_settings; diff --git a/lib/Cleantalk/ApbctWP/ServiceConstants.php b/lib/Cleantalk/ApbctWP/ServiceConstants.php index 84b4b4b77..a59f4250e 100644 --- a/lib/Cleantalk/ApbctWP/ServiceConstants.php +++ b/lib/Cleantalk/ApbctWP/ServiceConstants.php @@ -19,6 +19,63 @@ class ServiceConstants * @psalm-suppress PossiblyUnusedProperty */ public $set_ajax_route_type; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $self_owned_access_key; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $place_public_js_scripts_in_footer; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $whitelabel_faq_link; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $whitelabel_plugin_description; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + * @deprecated + */ + public $whitelabel_enabled; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + * @deprecated + */ + public $whitelabel_product_name; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $sfw_force_direct_update; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $disable_blocking_title; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $skip_on_approved_comments_number; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $predefined_api_url; + /** + * @var ApbctConstant + * @psalm-suppress PossiblyUnusedProperty + */ + public $do_not_collect_frontend_data_logs; /** * Is BotDetector enabled/disabled @@ -33,6 +90,7 @@ public function __construct() array( 'APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', ), + 'bool', 'If set, do not skip POST data from check if no email address found' ); $this->skip_anticrawler_on_rss_feed = new ApbctConstant( @@ -40,6 +98,7 @@ public function __construct() 'APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED', 'APBCT_ANTICRAWLER_EXLC_FEED', ), + 'bool', 'Pass anti-crawler check on RSS feed service' ); $this->set_ajax_route_type = new ApbctConstant( @@ -47,59 +106,93 @@ public function __construct() 'APBCT_SERVICE__SET_AJAX_ROUTE_TYPE', 'APBCT_SET_AJAX_ROUTE_TYPE', ), + 'string', 'Provides AJAX route type' ); $this->bot_detector_enabled = new ApbctConstant( array('APBCT_SERVICE__BOT_DETECTOR_ENABLED'), 'Allows to set Bot-Detector enabled/disabled' ); -// $accepted_constants = array( -// // needs to be refactored -// 'APBCT_SERVICE__SELF_OWNED_ACCESS_KEY' => array( -// 'deprecated_name' => 'CLEANTALK_ACCESS_KEY', -// 'description' => 'Provides user own access key.', -// ), -// 'APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER' => array( -// 'deprecated_name' => 'CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', -// 'description' => 'If defined, public scripts will be placed in footer instead of header', -// ), -// 'APBCT_SERVICE__WHITELABEL_FAQ_LINK' => array( -// 'deprecated_name' => 'APBCT_WHITELABEL_FAQ_LINK', -// 'description' => 'Provides whitelabel-mode FAQ link', -// ), -// 'APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION' => array( -// 'deprecated_name' => 'APBCT_WHITELABEL_PLUGIN_DESCRIPTION', -// 'description' => 'Provides whitelabel-mode plugin description', -// ), -// 'APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE' => array( -// 'deprecated_name' => 'APBCT_SFW_FORCE_DIRECT_UPDATE', -// 'description' => 'If defined, SFW update mode is always DIRECT', -// ), -// 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE' => array( -// 'deprecated_name' => 'CLEANTALK_DISABLE_BLOCKING_TITLE', -// 'description' => 'If defined, no title will be provided for blocking page.', -// ), -// 'APBCT_SERVICE__CHECK_COMMENTS_NUMBER' => array( -// 'deprecated_name' => 'CLEANTALK_CHECK_COMMENTS_NUMBER', -// 'description' => 'Provides how many comments should be approved before skip checking', -// ), -// 'APBCT_SERVICE__WHITELABEL' => array( -// 'deprecated_name' => 'APBCT_WHITELABEL', -// 'description' => 'If defined, plugin will be in whitelabel mode', -// ), -// 'APBCT_SERVICE__WHITELABEL_NAME' => array( -// 'deprecated_name' => 'APBCT_WHITELABEL_NAME', -// 'description' => 'Provides product name for whitelabel mode', -// ), -// 'APBCT_SERVICE__API_URL' => array( -// 'deprecated_name' => 'CLEANTALK_API_URL', -// 'description' => 'Provides own URL of API server', -// ), -// 'APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS' => array( -// 'deprecated_name' => 'APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS', -// 'description' => 'If defined, no frontend-data logs will be collected. Debugging case usage.', + $this->place_public_js_scripts_in_footer = new ApbctConstant( + array( + 'APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', + 'CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', + ), + 'bool', + 'If isset, any public scripts will be placed in a page footer.' + ); + $this->whitelabel_faq_link = new ApbctConstant( + array( + 'APBCT_SERVICE__WHITELABEL_FAQ_LINK', + 'APBCT_WHITELABEL_FAQ_LINK', + ), + 'string', + 'Provides whitelabel-mode FAQ link' + ); + $this->whitelabel_plugin_description = new ApbctConstant( + array( + 'APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION', + 'APBCT_WHITELABEL_PLUGIN_DESCRIPTION', + ), + 'string', + 'Provides whitelabel-mode plugin description.' + ); + $this->sfw_force_direct_update = new ApbctConstant( + array( + 'APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE', + 'APBCT_SFW_FORCE_DIRECT_UPDATE', + ), + 'bool', + 'If defined, SFW update mode is always DIRECT. Helpful if update queue fails due remote calls.' + ); + $this->disable_blocking_title = new ApbctConstant( + array( + 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE', + 'CLEANTALK_DISABLE_BLOCKING_TITLE', + ), + 'bool', + 'If defined, no title will be provided for blocking page.' + ); + $this->skip_on_approved_comments_number = new ApbctConstant( + array( + 'APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER', + 'CLEANTALK_CHECK_COMMENTS_NUMBER', + ), + 'int', + 'Redefine how many comments should be approved before skip checking.' + ); + $this->whitelabel_enabled = new ApbctConstant( + array( + 'APBCT_SERVICE__WHITELABEL_ENABLED', + 'APBCT_WHITELABEL', + ), + 'bool', + 'If defined, plugin will be in whitelabel mode.' + ); + $this->whitelabel_product_name = new ApbctConstant( + array( + 'APBCT_SERVICE__WHITELABEL_PRODUCT_NAME', + 'APBCT_WHITELABEL_NAME', + ), + 'string', + 'Provides product name for whitelabel mode.' + ); +// todo this won't work because constant called before State is initialized +// $this->predefined_cleantalk_server_url= new ApbctConstant( +// array( +// 'APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL', +// 'CLEANTALK_SERVER', // ), +// 'Provides own URL of API server.' // ); + $this->do_not_collect_frontend_data_logs = new ApbctConstant( + array( + 'APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS', + 'APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS', + ), + 'bool', + 'If defined, no frontend-data logs will be collected. Debugging case usage.' + ); } /** diff --git a/lib/Cleantalk/ApbctWP/State.php b/lib/Cleantalk/ApbctWP/State.php index af42df288..7935ebd73 100644 --- a/lib/Cleantalk/ApbctWP/State.php +++ b/lib/Cleantalk/ApbctWP/State.php @@ -7,7 +7,6 @@ use Cleantalk\Antispam\Integrations\NextendSocialLogin; use Cleantalk\ApbctWP\FindSpam\LoginIPKeeper; use Cleantalk\ApbctWP\Firewall\SFWUpdateSentinel; -use Cleantalk\ApbctWP\ServiceConstants; /** * CleanTalk Anti-Spam State class @@ -401,7 +400,7 @@ class State extends \Cleantalk\Common\State /** * @var ServiceConstants */ - public $service_constants; + public $constants; private $auto_save_defaults_list = array(); @@ -549,7 +548,7 @@ protected function setDefinitions() define('APBCT_SFW_SEND_LOGS_LIMIT', 1000); } - $this->service_constants = new ServiceConstants(); + $this->constants = new ServiceConstants(); } protected function setOptions() diff --git a/tests/ApbctWP/TestServiceConstants.php b/tests/ApbctWP/TestServiceConstants.php index 65467075a..011f9f28a 100644 --- a/tests/ApbctWP/TestServiceConstants.php +++ b/tests/ApbctWP/TestServiceConstants.php @@ -53,9 +53,9 @@ public function testGetDefinitionsActiveReturnsOnlyActiveConstantsLive() $this->assertIsArray($activeDefinitions); $this->assertCount(1, $activeDefinitions); $this->assertEquals('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', $activeDefinitions[0]['is_defined']); - $this->assertEquals('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', $this->serviceConstants->disable_empty_email_exception->isDefined()); - $this->assertEquals('1', $activeDefinitions[0]['value']); - $this->assertEquals('1', $this->serviceConstants->disable_empty_email_exception->getValue()); + $this->assertEquals(true, $this->serviceConstants->disable_empty_email_exception->isDefined()); + $this->assertEquals(true, $activeDefinitions[0]['value']); + $this->assertEquals(true, $this->serviceConstants->disable_empty_email_exception->getValue()); $this->assertNotEmpty($activeDefinitions[0]['description']); define('APBCT_ANTICRAWLER_EXLC_FEED', true); @@ -65,8 +65,8 @@ public function testGetDefinitionsActiveReturnsOnlyActiveConstantsLive() $this->assertCount(2, $activeDefinitions); $this->assertEquals('APBCT_ANTICRAWLER_EXLC_FEED', $activeDefinitions[1]['is_defined']); $this->assertEquals('APBCT_ANTICRAWLER_EXLC_FEED', $this->serviceConstants->skip_anticrawler_on_rss_feed->isDefined()); - $this->assertEquals('1', $activeDefinitions[1]['value']); - $this->assertEquals('1', $this->serviceConstants->skip_anticrawler_on_rss_feed->getValue()); + $this->assertEquals(true, $activeDefinitions[1]['value']); + $this->assertEquals(true, $this->serviceConstants->skip_anticrawler_on_rss_feed->getValue()); $this->assertNotEmpty($activeDefinitions[1]['description']); define('APBCT_SET_AJAX_ROUTE_TYPE', 'admin_ajax'); @@ -75,9 +75,31 @@ public function testGetDefinitionsActiveReturnsOnlyActiveConstantsLive() $this->assertIsArray($activeDefinitions); $this->assertCount(3, $activeDefinitions); $this->assertEquals('APBCT_SET_AJAX_ROUTE_TYPE', $activeDefinitions[2]['is_defined']); - $this->assertEquals('APBCT_SET_AJAX_ROUTE_TYPE', $this->serviceConstants->set_ajax_route_type->isDefined()); + $this->assertEquals(true, $this->serviceConstants->set_ajax_route_type->isDefinedAndTypeOK()); $this->assertEquals('admin_ajax', $activeDefinitions[2]['value']); $this->assertEquals('admin_ajax', $this->serviceConstants->set_ajax_route_type->getValue()); $this->assertNotEmpty($activeDefinitions[2]['description']); + + define('CLEANTALK_ACCESS_KEY', 'asdasdasd'); + $this->serviceConstants = new ServiceConstants(); + $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); + $this->assertIsArray($activeDefinitions); + $this->assertCount(4, $activeDefinitions); + $this->assertEquals('CLEANTALK_ACCESS_KEY', $activeDefinitions[3]['is_defined']); + $this->assertEquals(true, $this->serviceConstants->self_owned_access_key->isDefinedAndTypeOK()); + $this->assertEquals('asdasdasd', $activeDefinitions[3]['value']); + $this->assertEquals('asdasdasd', $this->serviceConstants->self_owned_access_key->getValue()); + $this->assertNotEmpty($activeDefinitions[3]['description']); + + define('CLEANTALK_CHECK_COMMENTS_NUMBER', false); + $this->serviceConstants = new ServiceConstants(); + $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); + $this->assertIsArray($activeDefinitions); + $this->assertCount(5, $activeDefinitions); + $this->assertEquals('CLEANTALK_CHECK_COMMENTS_NUMBER', $activeDefinitions[4]['is_defined']); + $this->assertEquals(false, $this->serviceConstants->skip_on_approved_comments_number->isDefinedAndTypeOK()); + $this->assertEquals(false, $activeDefinitions[4]['value']); + $this->assertEquals(false, $this->serviceConstants->skip_on_approved_comments_number->getValue()); + $this->assertNotEmpty($activeDefinitions[4]['description']); } } From 9ff83ba7e4f5f4a451cc4889a5f76afecdce511c Mon Sep 17 00:00:00 2001 From: datorik Date: Mon, 3 Aug 2026 17:15:14 +0300 Subject: [PATCH 2/8] New.Code.Constants. Error fix --- inc/cleantalk-common.php | 4 ++-- inc/cleantalk-settings.php | 2 +- lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php | 4 ++-- .../ApbctWP/Localize/CtPublicFunctionsLocalize.php | 5 +++-- lib/Cleantalk/ApbctWP/ServiceConstants.php | 9 +++++++++ tests/ApbctWP/Localize/TestCtPublicFunctionsLocalize.php | 4 +++- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/inc/cleantalk-common.php b/inc/cleantalk-common.php index afab7c29f..b8cadebcb 100644 --- a/inc/cleantalk-common.php +++ b/inc/cleantalk-common.php @@ -1933,8 +1933,8 @@ function apbct__is_bot_detector_enabled() global $apbct; // Constant is preferred - if ( isset($apbct->service_constants->bot_detector_enabled) && $apbct->service_constants->bot_detector_enabled->isDefined() ) { - return (bool) $apbct->service_constants->bot_detector_enabled->getValue(); + if ( isset($apbct->constants->bot_detector_enabled) && $apbct->constants->bot_detector_enabled->isDefined() ) { + return (bool) $apbct->constants->bot_detector_enabled->getValue(); } // Check by $apbct->data if ( isset($apbct->data['bot_detector_enabled']) ) { diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php index 79b1fa36c..8c2a0c855 100644 --- a/inc/cleantalk-settings.php +++ b/inc/cleantalk-settings.php @@ -2434,7 +2434,7 @@ function apbct_settings__validate($incoming_settings) : false; $incoming_settings['apikey'] = ! empty($incoming_settings['apikey']) ? trim($incoming_settings['apikey']) : ''; - $incoming_settings['apikey'] = defined('CLEANTALK_ACCESS_KEY') ? CLEANTALK_ACCESS_KEY : $incoming_settings['apikey']; + $incoming_settings['apikey'] = $predefined_key !== false ? $predefined_key : $incoming_settings['apikey']; $incoming_settings['apikey'] = ! is_main_site() && $apbct->white_label && $apbct->settings['apikey'] ? $apbct->settings['apikey'] : $incoming_settings['apikey']; $incoming_settings['apikey'] = is_main_site() || $apbct->allow_custom_key || $apbct->white_label ? $incoming_settings['apikey'] : $apbct->network_settings['apikey']; $incoming_settings['apikey'] = is_main_site() || ! isset($incoming_settings['multisite__white_label']) || ! $incoming_settings['multisite__white_label'] diff --git a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php index 157a00908..4c3c70f3a 100644 --- a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php +++ b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php @@ -709,13 +709,13 @@ private function checkExclusions() } // skip for RSS Feed requests - if ($apbct->constants->skip_anticrawler_on_rss_feed->isDefined()) { + if ($this->apbct->constants->skip_anticrawler_on_rss_feed->isDefined()) { if (Server::getString('REQUEST_URI') && preg_match_all('/feed/i', Server::getString('REQUEST_URI')) ) { $this->debug( 'exclusions precheck: RSS feed requests disabled by service constant', - $this->apbct->service_constants->skip_anticrawler_on_rss_feed->allowed_public_names + $this->apbct->constants->skip_anticrawler_on_rss_feed->allowed_public_names ); return true; } diff --git a/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php b/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php index 9669a106d..329b71923 100644 --- a/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php +++ b/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php @@ -2,6 +2,7 @@ namespace Cleantalk\ApbctWP\Localize; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\ContactsEncoder\ContactsEncoder; use Cleantalk\ApbctWP\Escape; use Cleantalk\ApbctWP\Variables\Server; @@ -23,8 +24,8 @@ public static function getData() 'data__cookies_type' => $apbct->data['cookies_type'], 'data__ajax_type' => $apbct->data['ajax_type'], 'bot_detector_enabled' => apbct__is_bot_detector_enabled(), - 'data__frontend_data_log_enabled' => defined('APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS') ? 0 : 1, - 'data__bot_detector_enabled' => $apbct->settings['data__bot_detector_enabled'], + 'data__frontend_data_log_enabled' => Constant::is(Constant::APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS) ? 0 : 1, + 'data__bot_detector_enabled' => apbct__is_bot_detector_enabled() ? 1 : 0, 'cookiePrefix' => apbct__get_cookie_prefix(), 'wprocket_detected' => apbct_is_plugin_active('wp-rocket/wp-rocket.php'), 'host_url' => Server::get('HTTP_HOST'), diff --git a/lib/Cleantalk/ApbctWP/ServiceConstants.php b/lib/Cleantalk/ApbctWP/ServiceConstants.php index a59f4250e..52fc73ac0 100644 --- a/lib/Cleantalk/ApbctWP/ServiceConstants.php +++ b/lib/Cleantalk/ApbctWP/ServiceConstants.php @@ -109,8 +109,17 @@ public function __construct() 'string', 'Provides AJAX route type' ); + $this->self_owned_access_key = new ApbctConstant( + array( + 'APBCT_SERVICE__SELF_OWNED_ACCESS_KEY', + 'CLEANTALK_ACCESS_KEY', + ), + 'string', + 'Provides user own access key.' + ); $this->bot_detector_enabled = new ApbctConstant( array('APBCT_SERVICE__BOT_DETECTOR_ENABLED'), + 'bool', 'Allows to set Bot-Detector enabled/disabled' ); $this->place_public_js_scripts_in_footer = new ApbctConstant( diff --git a/tests/ApbctWP/Localize/TestCtPublicFunctionsLocalize.php b/tests/ApbctWP/Localize/TestCtPublicFunctionsLocalize.php index dae16de11..8712a33a2 100644 --- a/tests/ApbctWP/Localize/TestCtPublicFunctionsLocalize.php +++ b/tests/ApbctWP/Localize/TestCtPublicFunctionsLocalize.php @@ -28,6 +28,8 @@ public function testGetData() // Assert $this->assertArrayHasKey('bot_detector_enabled', $localize_data); - $this->assertArrayNotHasKey('data__bot_detector_enabled', $localize_data); + $this->assertArrayHasKey('data__bot_detector_enabled', $localize_data); + // The setting itself is gone since 6.76.0, the value is computed - see apbct__is_bot_detector_enabled() + $this->assertContains($localize_data['data__bot_detector_enabled'], array(0, 1)); } } \ No newline at end of file From f20fff834534c3bb878ea77cfe867b06cea447d1 Mon Sep 17 00:00:00 2001 From: datorik Date: Tue, 4 Aug 2026 14:29:24 +0300 Subject: [PATCH 3/8] New.Code.New constants. --- cleantalk.php | 13 +- inc/cleantalk-common.php | 9 +- inc/cleantalk-pluggable.php | 8 +- inc/cleantalk-public.php | 13 +- inc/cleantalk-settings.php | 15 +- .../CleantalkPreprocessComment.php | 12 +- .../IntegrationsByClass/BuddyPress.php | 5 +- lib/Cleantalk/ApbctWP/Activator.php | 2 +- lib/Cleantalk/ApbctWP/AdminNotices.php | 2 +- lib/Cleantalk/ApbctWP/ApbctConstant.php | 91 ---- lib/Cleantalk/ApbctWP/Constant.php | 471 ++++++++++++++++++ .../ApbctWP/Firewall/AntiCrawler.php | 5 +- lib/Cleantalk/ApbctWP/RemoteCalls.php | 2 +- lib/Cleantalk/ApbctWP/ServiceConstants.php | 237 --------- lib/Cleantalk/ApbctWP/State.php | 6 - tests/ApbctWP/TestServiceConstants.php | 105 ---- tests/Inc/TestCleantalkCommon.php | 41 +- 17 files changed, 530 insertions(+), 507 deletions(-) delete mode 100644 lib/Cleantalk/ApbctWP/ApbctConstant.php create mode 100644 lib/Cleantalk/ApbctWP/Constant.php delete mode 100644 lib/Cleantalk/ApbctWP/ServiceConstants.php delete mode 100644 tests/ApbctWP/TestServiceConstants.php diff --git a/cleantalk.php b/cleantalk.php index c0aa328f2..468867ae1 100644 --- a/cleantalk.php +++ b/cleantalk.php @@ -15,6 +15,7 @@ use Cleantalk\Antispam\ProtectByShortcode; use Cleantalk\ApbctWP\Activator; use Cleantalk\ApbctWP\AdminNotices; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\ContactsEncoder\ContactsEncoder; use Cleantalk\ApbctWP\Antispam\ForceProtection; use Cleantalk\ApbctWP\API; @@ -275,7 +276,7 @@ function apbct_register_my_rest_routes() // Database prefix global $wpdb, $wp_version; $apbct->db_prefix = ! APBCT_WPMS || $apbct->allow_custom_key || $apbct->white_label ? $wpdb->prefix : $wpdb->base_prefix; -$apbct->db_prefix = ! $apbct->white_label && $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ? $wpdb->base_prefix : $wpdb->prefix; +$apbct->db_prefix = ! $apbct->white_label && Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) ? $wpdb->base_prefix : $wpdb->prefix; /** @todo HARDCODE FIX */ if ( $apbct->plugin_version === '1.0.0' ) { @@ -1265,7 +1266,7 @@ function apbct_sfw_update__switch_to_direct() $apbct->fw_stats['reason_direct_update_log'] = null; - if ($apbct->constants->sfw_force_direct_update->isDefined()) { + if (Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE)) { $apbct->fw_stats['reason_direct_update_log'] = 'constant exists'; return true; } @@ -2820,15 +2821,15 @@ function ct_account_status_check($api_key = null, $process_errors = true) : 0; //todo:temporary solution for description, until we found the way to transfer this from cloud - if ($apbct->constants->whitelabel_plugin_description->isDefinedAndTypeOK()) { + if (Constant::is(Constant::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION)) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_antispam_description'] = esc_html($apbct->constants->whitelabel_plugin_description->getValue()); + $result['wl_antispam_description'] = esc_html(Constant::getValue(Constant::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION)); } //todo:temporary solution for FAQ - if ($apbct->constants->whitelabel_faq_link->isDefinedAndTypeOK()) { + if (Constant::is(Constant::APBCT_SERVICE__WHITELABEL_FAQ_LINK)) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_faq_url'] = esc_url($apbct->constants->whitelabel_faq_link->getValue()); + $result['wl_faq_url'] = esc_url(Constant::getValue(Constant::APBCT_SERVICE__WHITELABEL_FAQ_LINK)); } if ( isset($result['wl_status']) && $result['wl_status'] === 'ON' ) { diff --git a/inc/cleantalk-common.php b/inc/cleantalk-common.php index b8cadebcb..808e9bf46 100644 --- a/inc/cleantalk-common.php +++ b/inc/cleantalk-common.php @@ -6,6 +6,7 @@ use Cleantalk\ApbctWP\API; use Cleantalk\ApbctWP\BaseCall\DefaultParams; use Cleantalk\ApbctWP\CleantalkSettingsTemplates; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\Cron; use Cleantalk\ApbctWP\DB; use Cleantalk\ApbctWP\DTO\GetFieldsAnyDTO; @@ -218,7 +219,7 @@ function apbct_base_call($params = array(), $reg_flag = false) * @since 6.58.99 */ if ( - $apbct->constants->disable_empty_email_exception->isDefined() && + Constant::is(Constant::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION) && !$apbct->settings['data__general_postdata_test'] ) { $params['exception_action'] = 0; @@ -1858,7 +1859,7 @@ function apbct__bot_detector_get_fd_log() ); // Initialize result array with default values - if ($apbct->constants->do_not_collect_frontend_data_logs->isDefined()) { + if (Constant::is(Constant::APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS)) { $result['plugin_status'] = 'OK'; $result['error_msg'] = 'bot detector logs collection is disabled via constant definition'; return json_encode($result); @@ -1933,8 +1934,8 @@ function apbct__is_bot_detector_enabled() global $apbct; // Constant is preferred - if ( isset($apbct->constants->bot_detector_enabled) && $apbct->constants->bot_detector_enabled->isDefined() ) { - return (bool) $apbct->constants->bot_detector_enabled->getValue(); + if ( Constant::is(Constant::APBCT_SERVICE__BOT_DETECTOR_ENABLED) ) { + return (bool) Constant::getValue(Constant::APBCT_SERVICE__BOT_DETECTOR_ENABLED); } // Check by $apbct->data if ( isset($apbct->data['bot_detector_enabled']) ) { diff --git a/inc/cleantalk-pluggable.php b/inc/cleantalk-pluggable.php index 5bfa7e8eb..218b0dc90 100644 --- a/inc/cleantalk-pluggable.php +++ b/inc/cleantalk-pluggable.php @@ -1,6 +1,7 @@ constants->set_ajax_route_type->isDefinedAndTypeOK() - && in_array($apbct->constants->set_ajax_route_type->getValue(), array('rest','admin_ajax')) - ) { - return $apbct->constants->set_ajax_route_type->getValue(); + $forced_ajax_route_type = Constant::getValue(Constant::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE); + if ( in_array($forced_ajax_route_type, array('rest', 'admin_ajax'), true) ) { + return $forced_ajax_route_type; } // Check rest availability diff --git a/inc/cleantalk-public.php b/inc/cleantalk-public.php index 57584e837..46cba54c1 100644 --- a/inc/cleantalk-public.php +++ b/inc/cleantalk-public.php @@ -1,6 +1,7 @@ constants->place_public_js_scripts_in_footer->isDefined()) { + if (Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER)) { add_action('wp_footer', array(LocalizeHandler::class, 'handle'), 1); add_action('login_footer', array(LocalizeHandler::class, 'handle'), 1); } else { @@ -811,7 +812,7 @@ function apbct_comment__wordpress__show_blacklists($notify_message, $comment_id) */ function ct_die($_comment_id, $_comment_status) { - global $ct_comment, $ct_jp_comments, $apbct; + global $ct_comment, $ct_jp_comments; // JCH Optimize caching preventing add_filter('jch_optimize_page_cache_set_caching', static function ($_is_cache_active) { @@ -821,7 +822,7 @@ function ct_die($_comment_id, $_comment_status) do_action('apbct_pre_block_page', $ct_comment); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( ! $apbct->constants->disable_blocking_title->isDefined() ) { + if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) ) { $message_title = 'CleanTalk. ' . $message_title; } if ( Post::get('et_pb_contact_email') ) { @@ -870,7 +871,7 @@ function ct_die($_comment_id, $_comment_status) */ function ct_die_extended($comment_body) { - global $ct_jp_comments, $apbct; + global $ct_jp_comments; // JCH Optimize caching preventing add_filter('jch_optimize_page_cache_set_caching', static function ($_is_cache_active) { @@ -878,7 +879,7 @@ function ct_die_extended($comment_body) }, 999, 1); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( !$apbct->constants->disable_blocking_title->isDefined() ) { + if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) ) { $message_title = 'CleanTalk. ' . $message_title; } @@ -1263,7 +1264,7 @@ function apbct_enqueue_and_localize_public_scripts() { global $apbct; - $in_footer = $apbct->constants->place_public_js_scripts_in_footer->isDefined(); + $in_footer = Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER); // Different JS params $bundle_name = ApbctJsBundleResolver::getBundleName($apbct->settings); ApbctEnqueue::getInstance()->js($bundle_name, array(), $in_footer); diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php index 8c2a0c855..00d6b2380 100644 --- a/inc/cleantalk-settings.php +++ b/inc/cleantalk-settings.php @@ -3,6 +3,7 @@ use Cleantalk\ApbctWP\AdjustToEnvironmentModule\AdjustToEnvironmentHandler; use Cleantalk\ApbctWP\AdjustToEnvironmentModule\AdjustToEnvironmentSettings; use Cleantalk\ApbctWP\AJAXService; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\ContactsEncoder\ContactsEncoder; use Cleantalk\ApbctWP\Escape; use Cleantalk\ApbctWP\Helper; @@ -492,9 +493,7 @@ function apbct_settings__set_fields() 'title' => __("Don't check trusted user's comments", 'cleantalk-spam-protect'), 'description' => sprintf( __("Don't check comments for users with above %d comments.", 'cleantalk-spam-protect'), - $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() - ? $apbct->constants->skip_on_approved_comments_number->getValue() - : 3 + Constant::getValue(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, 3) ), ), 'data__use_ajax' => array( @@ -1150,7 +1149,7 @@ function apbct_settings__set_fields__network($fields) '' ), 'childrens' => array('multisite__white_label__plugin_name'), - 'disabled' => $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() || + 'disabled' => Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) || ! isset($apbct->network_settings['multisite__work_mode']) || $apbct->network_settings['multisite__work_mode'] != 1, 'parent' => 'multisite__work_mode', @@ -1759,7 +1758,7 @@ function apbct_settings__error__output($return = false) $out = ''; - if ( ! empty($apbct->errors) && ! $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ) { + if ( ! empty($apbct->errors) && ! Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) ) { $errors = $apbct->errors; $error_texts = array( @@ -2055,7 +2054,7 @@ function apbct_settings__field__apikey() $define_key_is_provided_by_admin = APBCT_WPMS && ! is_main_site() && ( ! $apbct->allow_custom_key || - $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() + Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) ); $define_show_key_field = ! (apbct_api_key__is_correct($apbct->api_key) && isset($apbct->data["key_changed"]) && $apbct->data["key_changed"]); $define_show_deobfuscating_href = apbct_api_key__is_correct($apbct->api_key) && $apbct->key_is_ok && (!isset($apbct->data["key_changed"]) || !$apbct->data["key_changed"]); @@ -2429,9 +2428,7 @@ function apbct_settings__validate($incoming_settings) $apbct->data['key_changed'] = $incoming_settings['apikey'] !== $apbct->settings['apikey']; - $predefined_key = $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() - ? $apbct->constants->self_owned_access_key->getValue() - : false; + $predefined_key = Constant::getValue(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY, false); $incoming_settings['apikey'] = ! empty($incoming_settings['apikey']) ? trim($incoming_settings['apikey']) : ''; $incoming_settings['apikey'] = $predefined_key !== false ? $predefined_key : $incoming_settings['apikey']; diff --git a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php index 14be09f45..063fee3a0 100644 --- a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php +++ b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php @@ -3,6 +3,7 @@ namespace Cleantalk\Antispam\Integrations; use Cleantalk\ApbctWP\CleantalkRealPerson; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\Sanitize; use Cleantalk\ApbctWP\Variables\AltSessions; use Cleantalk\ApbctWP\Variables\Cookie; @@ -64,9 +65,10 @@ public function doPrepareActions($argument) $this->wp_comment_post_id = $comment_post_id; $this->post_info = array(); - $this->comments_check_number_needs_to_skip_request = $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() - ? $apbct->constants->skip_on_approved_comments_number->getValue() - : 3; + $this->comments_check_number_needs_to_skip_request = Constant::getValue( + Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, + 3 + ); /** * Custom mail notifications processing @@ -317,7 +319,7 @@ public function doBlock($message) { $ct_result = $this->base_call_result['ct_result']; - global $ct_comment, $ct_stop_words, $apbct; + global $ct_comment, $ct_stop_words; $ct_comment = $message; $ct_stop_words = $ct_result->stop_words; /** @@ -332,7 +334,7 @@ public function doBlock($message) $err_text = '
' . ( - $apbct->constants->disable_blocking_title->isDefined() + Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) ? '' : 'CleanTalk. ' ) diff --git a/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php b/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php index 04821cc62..395b80b3e 100755 --- a/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php +++ b/lib/Cleantalk/Antispam/IntegrationsByClass/BuddyPress.php @@ -2,6 +2,7 @@ namespace Cleantalk\Antispam\IntegrationsByClass; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\Escape; use Cleantalk\ApbctWP\Variables\Post; use Cleantalk\ApbctWP\Variables\Server; @@ -78,9 +79,7 @@ public function privateMsgCheck($bp_message_obj) } // Check for quantity of comments - $comments_check_number = $apbct->constants->skip_on_approved_comments_number->isDefinedAndTypeOK() - ? $apbct->constants->skip_on_approved_comments_number->getValue() - : 3; + $comments_check_number = Constant::getValue(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, 3); if ( $apbct->settings['comments__check_comments_number'] ) { $args = array( diff --git a/lib/Cleantalk/ApbctWP/Activator.php b/lib/Cleantalk/ApbctWP/Activator.php index 0e88e4b1c..45aaf2891 100644 --- a/lib/Cleantalk/ApbctWP/Activator.php +++ b/lib/Cleantalk/ApbctWP/Activator.php @@ -26,7 +26,7 @@ public static function activation($network_wide, $concrete_blog_id = null) $apbct->stats['plugin']['activation__times'] += 1; $apbct->save('stats'); - if ( $network_wide && ! $apbct->constants->self_owned_access_key->isDefinedAndTypeOK() ) { + if ( $network_wide && ! Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) ) { $initial_blog = get_current_blog_id(); $blogs = array_keys($wpdb->get_results('SELECT blog_id FROM ' . $wpdb->blogs, OBJECT_K)); foreach ( $blogs as $blog ) { diff --git a/lib/Cleantalk/ApbctWP/AdminNotices.php b/lib/Cleantalk/ApbctWP/AdminNotices.php index 7eee425d8..c6931e579 100644 --- a/lib/Cleantalk/ApbctWP/AdminNotices.php +++ b/lib/Cleantalk/ApbctWP/AdminNotices.php @@ -72,7 +72,7 @@ private function __construct() in_array(Get::get('page'), array('cleantalk', 'ct_check_spam', 'ct_check_users')); $this->user_token = $this->apbct->user_token ?: ''; - $self_owned_key = $this->apbct->moderate_ip == 0 && !$this->apbct->constants->self_owned_access_key->isDefinedAndTypeOK(); + $self_owned_key = $this->apbct->moderate_ip == 0 && ! Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY); $is_dashboard = is_network_admin() || is_admin(); $is_admin = current_user_can('activate_plugins'); $uid = get_current_user_id(); diff --git a/lib/Cleantalk/ApbctWP/ApbctConstant.php b/lib/Cleantalk/ApbctWP/ApbctConstant.php deleted file mode 100644 index d34787cf9..000000000 --- a/lib/Cleantalk/ApbctWP/ApbctConstant.php +++ /dev/null @@ -1,91 +0,0 @@ -allowed_public_names = $allowed_public_names; - $this->description = $description; - $this->type = $type; - $this->defined_name = $this->getDefinedName(); - } - - /** - * If defined, returns the name of the first defined constant from the allowed names. Return false if none of the constants are defined. - * @return string|false - */ - private function getDefinedName() - { - foreach ($this->allowed_public_names as $name) { - if (defined($name)) { - return $name; - } - } - return false; - } - - /** - * If defined and type is correct. - * @return bool - */ - public function isDefinedAndTypeOK() - { - return $this->defined_name && gettype(constant($this->defined_name)) === $this->type; - } - - - /** - * Return the fact of definition - * @return bool - * @psalm-suppress PossiblyUnusedMethod - */ - public function isDefined() - { - return (bool)$this->defined_name; - } - - /** - * Returns the value of the first defined constant from the allowed names. Return null if none of the constants are defined. - * - * @return mixed|null - */ - public function getValue() - { - if ($this->defined_name) { - return constant($this->defined_name); - } - return null; - } - - /** - * @return array - */ - public function getData() - { - return array( - 'is_defined' => $this->defined_name, - 'value' => $this->getValue(), - 'description' => $this->description, - ); - } -} diff --git a/lib/Cleantalk/ApbctWP/Constant.php b/lib/Cleantalk/ApbctWP/Constant.php new file mode 100644 index 000000000..f8be7c4bd --- /dev/null +++ b/lib/Cleantalk/ApbctWP/Constant.php @@ -0,0 +1,471 @@ +constants->->isDefinedAndTypeOK()` chain with a single static call: + * + * Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE) // just defined & of the declared type + * Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE, false) // ... and strictly equals false + * + * The registry holds no state and does not depend on Cleantalk\ApbctWP\State, so it is usable + * at any point of the bootstrap - including before $apbct is instantiated. + * + * Every constant is declared twice: under its canonical `APBCT_SERVICE__*` name and, when a legacy + * name is still supported, under that name carrying a deprecation tag. Both class constants resolve + * to the same registry entry, so outdated call sites keep working while an IDE/Psalm strikes them + * through and reports `DeprecatedConstant`. + */ +class Constant +{ + /** + * If set, do not skip POST data from check if no email address found. + */ + const APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION = 'APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION'; + + /** + * Pass anti-crawler check on RSS feed service. + */ + const APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED = 'APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED'; + + /** + * Pass anti-crawler check on RSS feed service. + * @deprecated Use self::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED + */ + const APBCT_ANTICRAWLER_EXLC_FEED = 'APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED'; + + /** + * Provides AJAX route type. Expected values: 'rest', 'admin_ajax'. + */ + const APBCT_SERVICE__SET_AJAX_ROUTE_TYPE = 'APBCT_SERVICE__SET_AJAX_ROUTE_TYPE'; + + /** + * Provides AJAX route type. Expected values: 'rest', 'admin_ajax'. + * @deprecated Use self::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE + */ + const APBCT_SET_AJAX_ROUTE_TYPE = 'APBCT_SERVICE__SET_AJAX_ROUTE_TYPE'; + + /** + * Provides user own access key. + */ + const APBCT_SERVICE__SELF_OWNED_ACCESS_KEY = 'APBCT_SERVICE__SELF_OWNED_ACCESS_KEY'; + + /** + * Provides user own access key. + * @deprecated Use self::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY + */ + const CLEANTALK_ACCESS_KEY = 'APBCT_SERVICE__SELF_OWNED_ACCESS_KEY'; + + /** + * Allows to set Bot-Detector enabled/disabled. + */ + const APBCT_SERVICE__BOT_DETECTOR_ENABLED = 'APBCT_SERVICE__BOT_DETECTOR_ENABLED'; + + /** + * If isset, any public scripts will be placed in a page footer. + */ + const APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER = 'APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER'; + + /** + * If isset, any public scripts will be placed in a page footer. + * @deprecated Use self::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER + */ + const CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER = 'APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER'; + + /** + * Provides whitelabel-mode FAQ link. + */ + const APBCT_SERVICE__WHITELABEL_FAQ_LINK = 'APBCT_SERVICE__WHITELABEL_FAQ_LINK'; + + /** + * Provides whitelabel-mode FAQ link. + * @deprecated Use self::APBCT_SERVICE__WHITELABEL_FAQ_LINK + */ + const APBCT_WHITELABEL_FAQ_LINK = 'APBCT_SERVICE__WHITELABEL_FAQ_LINK'; + + /** + * Provides whitelabel-mode plugin description. + */ + const APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION = 'APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION'; + + /** + * Provides whitelabel-mode plugin description. + * @deprecated Use self::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION + */ + const APBCT_WHITELABEL_PLUGIN_DESCRIPTION = 'APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION'; + + /** + * If defined, plugin will be in whitelabel mode. + */ + const APBCT_SERVICE__WHITELABEL_ENABLED = 'APBCT_SERVICE__WHITELABEL_ENABLED'; + + /** + * If defined, plugin will be in whitelabel mode. + * @deprecated Use self::APBCT_SERVICE__WHITELABEL_ENABLED + */ + const APBCT_WHITELABEL = 'APBCT_SERVICE__WHITELABEL_ENABLED'; + + /** + * Provides product name for whitelabel mode. + */ + const APBCT_SERVICE__WHITELABEL_PRODUCT_NAME = 'APBCT_SERVICE__WHITELABEL_PRODUCT_NAME'; + + /** + * Provides product name for whitelabel mode. + * @deprecated Use self::APBCT_SERVICE__WHITELABEL_PRODUCT_NAME + */ + const APBCT_WHITELABEL_NAME = 'APBCT_SERVICE__WHITELABEL_PRODUCT_NAME'; + + /** + * If defined, SFW update mode is always DIRECT. Helpful if update queue fails due remote calls. + */ + const APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE = 'APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE'; + + /** + * If defined, SFW update mode is always DIRECT. Helpful if update queue fails due remote calls. + * @deprecated Use self::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE + */ + const APBCT_SFW_FORCE_DIRECT_UPDATE = 'APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE'; + + /** + * If defined, no title will be provided for blocking page. + */ + const APBCT_SERVICE__DISABLE_BLOCKING_TITLE = 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE'; + + /** + * If defined, no title will be provided for blocking page. + * @deprecated Use self::APBCT_SERVICE__DISABLE_BLOCKING_TITLE + */ + const CLEANTALK_DISABLE_BLOCKING_TITLE = 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE'; + + /** + * Redefine how many comments should be approved before skip checking. + */ + const APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER = 'APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER'; + + /** + * Redefine how many comments should be approved before skip checking. + * @deprecated Use self::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER + */ + const CLEANTALK_CHECK_COMMENTS_NUMBER = 'APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER'; + + /** + * If defined, no frontend-data logs will be collected. Debugging case usage. + */ + const APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS = 'APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS'; + + /** + * If defined, no frontend-data logs will be collected. Debugging case usage. + * @deprecated Use self::APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS + */ + const APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS = 'APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS'; + + /** + * Provides own URL of API server. + */ + const APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL = 'APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL'; + + /** + * Provides own URL of API server. + * @deprecated Use self::APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL + */ + const CLEANTALK_SERVER = 'APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL'; + + /** + * Registry cache. Built once per request by self::getRegistry(). + * + * @var array[]|null + */ + private static $registry; + + /** + * Maps a declared type to what gettype() actually returns. + * + * @var array + */ + private static $type_aliases = array( + 'bool' => 'boolean', + 'int' => 'integer', + 'float' => 'double', + ); + + /** + * Is the constant usable: defined, of the declared type and - when $expected_value is passed - + * strictly equal to it. + * + * Passing no $expected_value means "the fact of definition is enough". Passing one makes the + * check value-aware, so `define('APBCT_SERVICE__DISABLE_BLOCKING_TITLE', false)` can be told + * apart from the constant being absent. + * + * @param string $constant One of the self::* class constants. + * @param mixed $expected_value Optional. Compared strictly (===) against the constant value. + * + * @return bool + */ + public static function is($constant, $expected_value = null) + { + $defined_name = self::getDefinedName($constant); + + if ( $defined_name === false ) { + return false; + } + + $value = constant($defined_name); + + if ( ! self::typeIsValid($constant, $value) ) { + return false; + } + + // Distinguishes "no second argument" from an explicit null/false, which a default cannot do. + if ( func_num_args() < 2 ) { + return true; + } + + return $value === $expected_value; + } + + /** + * Value of the constant, or $default when it is not defined or fails the type check. + * + * @param string $constant One of the self::* class constants. + * @param mixed $default + * + * @return mixed + */ + public static function getValue($constant, $default = null) + { + $defined_name = self::getDefinedName($constant); + + if ( $defined_name === false ) { + return $default; + } + + $value = constant($defined_name); + + return self::typeIsValid($constant, $value) ? $value : $default; + } + + /** + * All public names the constant may be defined under: the canonical one first, then legacy ones. + * Intended for diagnostics - reporting to the user which names are recognized. + * + * @param string $constant One of the self::* class constants. + * + * @return string[] + */ + public static function getNames($constant) + { + $registry = self::getRegistry(); + + return isset($registry[$constant]) ? $registry[$constant]['names'] : array(); + } + + /** + * All known service constants and their current state. + * + * @return array[] + */ + public static function getDefinitions() + { + $result = array(); + + foreach ( array_keys(self::getRegistry()) as $constant ) { + $defined_name = self::getDefinedName($constant); + $result[] = array( + 'is_defined' => $defined_name, + 'value' => $defined_name === false ? null : constant($defined_name), + 'description' => self::getRegistry()[$constant]['description'], + ); + } + + return $result; + } + + /** + * Only the constants that are currently defined. + * + * @return array[] + */ + public static function getDefinitionsActive() + { + $active = array(); + + foreach ( self::getDefinitions() as $definition ) { + if ( ! empty($definition['is_defined']) ) { + $active[] = $definition; + } + } + + return $active; + } + + /** + * Name of the first defined constant among the canonical one and its legacy aliases. + * + * @param string $constant One of the self::* class constants. + * + * @return string|false + */ + private static function getDefinedName($constant) + { + $registry = self::getRegistry(); + + if ( ! isset($registry[$constant]) ) { + return false; + } + + foreach ( $registry[$constant]['names'] as $name ) { + if ( defined($name) ) { + return $name; + } + } + + return false; + } + + /** + * @param string $constant One of the self::* class constants. + * @param mixed $value + * + * @return bool + */ + private static function typeIsValid($constant, $value) + { + $registry = self::getRegistry(); + $type = $registry[$constant]['type']; + $type = isset(self::$type_aliases[$type]) ? self::$type_aliases[$type] : $type; + + return gettype($value) === $type; + } + + /** + * Canonical name => allowed public names (canonical first, then legacy), declared type, description. + * + * @return array[] + */ + private static function getRegistry() + { + if ( self::$registry === null ) { + self::$registry = array( + self::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION => array( + 'names' => array(self::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION), + 'type' => 'bool', + 'description' => 'If set, do not skip POST data from check if no email address found', + ), + self::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED => array( + 'names' => array( + self::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED, + 'APBCT_ANTICRAWLER_EXLC_FEED', + ), + 'type' => 'bool', + 'description' => 'Pass anti-crawler check on RSS feed service', + ), + self::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE => array( + 'names' => array( + self::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE, + 'APBCT_SET_AJAX_ROUTE_TYPE', + ), + 'type' => 'string', + 'description' => 'Provides AJAX route type', + ), + self::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY => array( + 'names' => array( + self::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY, + 'CLEANTALK_ACCESS_KEY', + ), + 'type' => 'string', + 'description' => 'Provides user own access key.', + ), + self::APBCT_SERVICE__BOT_DETECTOR_ENABLED => array( + 'names' => array(self::APBCT_SERVICE__BOT_DETECTOR_ENABLED), + 'type' => 'bool', + 'description' => 'Allows to set Bot-Detector enabled/disabled', + ), + self::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER => array( + 'names' => array( + self::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER, + 'CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', + ), + 'type' => 'bool', + 'description' => 'If isset, any public scripts will be placed in a page footer.', + ), + self::APBCT_SERVICE__WHITELABEL_FAQ_LINK => array( + 'names' => array( + self::APBCT_SERVICE__WHITELABEL_FAQ_LINK, + 'APBCT_WHITELABEL_FAQ_LINK', + ), + 'type' => 'string', + 'description' => 'Provides whitelabel-mode FAQ link', + ), + self::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION => array( + 'names' => array( + self::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION, + 'APBCT_WHITELABEL_PLUGIN_DESCRIPTION', + ), + 'type' => 'string', + 'description' => 'Provides whitelabel-mode plugin description.', + ), + self::APBCT_SERVICE__WHITELABEL_ENABLED => array( + 'names' => array( + self::APBCT_SERVICE__WHITELABEL_ENABLED, + 'APBCT_WHITELABEL', + ), + 'type' => 'bool', + 'description' => 'If defined, plugin will be in whitelabel mode.', + ), + self::APBCT_SERVICE__WHITELABEL_PRODUCT_NAME => array( + 'names' => array( + self::APBCT_SERVICE__WHITELABEL_PRODUCT_NAME, + 'APBCT_WHITELABEL_NAME', + ), + 'type' => 'string', + 'description' => 'Provides product name for whitelabel mode.', + ), + self::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE => array( + 'names' => array( + self::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE, + 'APBCT_SFW_FORCE_DIRECT_UPDATE', + ), + 'type' => 'bool', + 'description' => 'If defined, SFW update mode is always DIRECT. Helpful if update queue fails due remote calls.', + ), + self::APBCT_SERVICE__DISABLE_BLOCKING_TITLE => array( + 'names' => array( + self::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, + 'CLEANTALK_DISABLE_BLOCKING_TITLE', + ), + 'type' => 'bool', + 'description' => 'If defined, no title will be provided for blocking page.', + ), + self::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER => array( + 'names' => array( + self::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, + 'CLEANTALK_CHECK_COMMENTS_NUMBER', + ), + 'type' => 'int', + 'description' => 'Redefine how many comments should be approved before skip checking.', + ), + self::APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS => array( + 'names' => array( + self::APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS, + 'APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS', + ), + 'type' => 'bool', + 'description' => 'If defined, no frontend-data logs will be collected. Debugging case usage.', + ), + self::APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL => array( + 'names' => array( + self::APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL, + 'CLEANTALK_SERVER', + ), + 'type' => 'string', + 'description' => 'Provides own URL of API server.', + ), + ); + } + + return self::$registry; + } +} \ No newline at end of file diff --git a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php index 4c3c70f3a..25e741827 100644 --- a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php +++ b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php @@ -2,6 +2,7 @@ namespace Cleantalk\ApbctWP\Firewall; +use Cleantalk\ApbctWP\Constant; use Cleantalk\ApbctWP\RequestParameters\RequestParameters; use Cleantalk\ApbctWP\State; use Cleantalk\ApbctWP\Validate; @@ -709,13 +710,13 @@ private function checkExclusions() } // skip for RSS Feed requests - if ($this->apbct->constants->skip_anticrawler_on_rss_feed->isDefined()) { + if (Constant::is(Constant::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED)) { if (Server::getString('REQUEST_URI') && preg_match_all('/feed/i', Server::getString('REQUEST_URI')) ) { $this->debug( 'exclusions precheck: RSS feed requests disabled by service constant', - $this->apbct->constants->skip_anticrawler_on_rss_feed->allowed_public_names + Constant::getNames(Constant::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED) ); return true; } diff --git a/lib/Cleantalk/ApbctWP/RemoteCalls.php b/lib/Cleantalk/ApbctWP/RemoteCalls.php index b4b70ef23..7c27f55f0 100644 --- a/lib/Cleantalk/ApbctWP/RemoteCalls.php +++ b/lib/Cleantalk/ApbctWP/RemoteCalls.php @@ -375,7 +375,7 @@ public static function action__debug() // phpcs:ignore PSR1.Methods.CamelCapsMet if ($apbct->settings['data__set_cookies'] == 3 && $apbct->data['cookies_type'] === 'alternative') { $out['alt_sessions_auto_state_reason'] = $apbct->isAltSessionsRequired(true); } - $out['active_service_constants'] = $apbct->constants->getDefinitionsActive(); + $out['active_service_constants'] = Constant::getDefinitionsActive(); if ( APBCT_WPMS ) { $out['network_settings'] = $apbct->network_settings; diff --git a/lib/Cleantalk/ApbctWP/ServiceConstants.php b/lib/Cleantalk/ApbctWP/ServiceConstants.php deleted file mode 100644 index 52fc73ac0..000000000 --- a/lib/Cleantalk/ApbctWP/ServiceConstants.php +++ /dev/null @@ -1,237 +0,0 @@ -disable_empty_email_exception = new ApbctConstant( - array( - 'APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', - ), - 'bool', - 'If set, do not skip POST data from check if no email address found' - ); - $this->skip_anticrawler_on_rss_feed = new ApbctConstant( - array( - 'APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED', - 'APBCT_ANTICRAWLER_EXLC_FEED', - ), - 'bool', - 'Pass anti-crawler check on RSS feed service' - ); - $this->set_ajax_route_type = new ApbctConstant( - array( - 'APBCT_SERVICE__SET_AJAX_ROUTE_TYPE', - 'APBCT_SET_AJAX_ROUTE_TYPE', - ), - 'string', - 'Provides AJAX route type' - ); - $this->self_owned_access_key = new ApbctConstant( - array( - 'APBCT_SERVICE__SELF_OWNED_ACCESS_KEY', - 'CLEANTALK_ACCESS_KEY', - ), - 'string', - 'Provides user own access key.' - ); - $this->bot_detector_enabled = new ApbctConstant( - array('APBCT_SERVICE__BOT_DETECTOR_ENABLED'), - 'bool', - 'Allows to set Bot-Detector enabled/disabled' - ); - $this->place_public_js_scripts_in_footer = new ApbctConstant( - array( - 'APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', - 'CLEANTALK_PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER', - ), - 'bool', - 'If isset, any public scripts will be placed in a page footer.' - ); - $this->whitelabel_faq_link = new ApbctConstant( - array( - 'APBCT_SERVICE__WHITELABEL_FAQ_LINK', - 'APBCT_WHITELABEL_FAQ_LINK', - ), - 'string', - 'Provides whitelabel-mode FAQ link' - ); - $this->whitelabel_plugin_description = new ApbctConstant( - array( - 'APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION', - 'APBCT_WHITELABEL_PLUGIN_DESCRIPTION', - ), - 'string', - 'Provides whitelabel-mode plugin description.' - ); - $this->sfw_force_direct_update = new ApbctConstant( - array( - 'APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE', - 'APBCT_SFW_FORCE_DIRECT_UPDATE', - ), - 'bool', - 'If defined, SFW update mode is always DIRECT. Helpful if update queue fails due remote calls.' - ); - $this->disable_blocking_title = new ApbctConstant( - array( - 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE', - 'CLEANTALK_DISABLE_BLOCKING_TITLE', - ), - 'bool', - 'If defined, no title will be provided for blocking page.' - ); - $this->skip_on_approved_comments_number = new ApbctConstant( - array( - 'APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER', - 'CLEANTALK_CHECK_COMMENTS_NUMBER', - ), - 'int', - 'Redefine how many comments should be approved before skip checking.' - ); - $this->whitelabel_enabled = new ApbctConstant( - array( - 'APBCT_SERVICE__WHITELABEL_ENABLED', - 'APBCT_WHITELABEL', - ), - 'bool', - 'If defined, plugin will be in whitelabel mode.' - ); - $this->whitelabel_product_name = new ApbctConstant( - array( - 'APBCT_SERVICE__WHITELABEL_PRODUCT_NAME', - 'APBCT_WHITELABEL_NAME', - ), - 'string', - 'Provides product name for whitelabel mode.' - ); -// todo this won't work because constant called before State is initialized -// $this->predefined_cleantalk_server_url= new ApbctConstant( -// array( -// 'APBCT_SERVICE__PREDEFINED_CLEANTALK_SERVER_URL', -// 'CLEANTALK_SERVER', -// ), -// 'Provides own URL of API server.' -// ); - $this->do_not_collect_frontend_data_logs = new ApbctConstant( - array( - 'APBCT_SERVICE__DO_NOT_COLLECT_FRONTEND_DATA_LOGS', - 'APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS', - ), - 'bool', - 'If defined, no frontend-data logs will be collected. Debugging case usage.' - ); - } - - /** - * Get all service constants definitions - * @return array[] - */ - public function getDefinitions() - { - $result = []; - foreach (get_object_vars($this) as $_key => $value) { - if ($value instanceof ApbctConstant) { - $result[] = $value->getData(); - } - } - return $result; - } - - /** - * Return active definitions. - * @return array - * @psalm-suppress PossiblyUnusedMethod - */ - public function getDefinitionsActive() - { - $active = array(); - foreach ($this->getDefinitions() as $constant) { - if (!empty($constant['is_defined'])) { - $active[] = $constant; - } - } - return $active; - } -} diff --git a/lib/Cleantalk/ApbctWP/State.php b/lib/Cleantalk/ApbctWP/State.php index 7935ebd73..e0d637726 100644 --- a/lib/Cleantalk/ApbctWP/State.php +++ b/lib/Cleantalk/ApbctWP/State.php @@ -397,10 +397,6 @@ class State extends \Cleantalk\Common\State * @var LoginIPKeeper */ public $login_ip_keeper; - /** - * @var ServiceConstants - */ - public $constants; private $auto_save_defaults_list = array(); @@ -547,8 +543,6 @@ protected function setDefinitions() // Limit for firewall logs sending. define('APBCT_SFW_SEND_LOGS_LIMIT', 1000); } - - $this->constants = new ServiceConstants(); } protected function setOptions() diff --git a/tests/ApbctWP/TestServiceConstants.php b/tests/ApbctWP/TestServiceConstants.php deleted file mode 100644 index 011f9f28a..000000000 --- a/tests/ApbctWP/TestServiceConstants.php +++ /dev/null @@ -1,105 +0,0 @@ -serviceConstants = new ServiceConstants(); - } - - public function testGetDefinitionsReturnsAllConstants() - { - $definitions = $this->serviceConstants->getDefinitions(); - - $this->assertIsArray($definitions); - $this->assertNotEmpty($definitions); - foreach ($definitions as $definition) { - $this->assertArrayHasKey('is_defined', $definition); - $this->assertArrayHasKey('value', $definition); - $this->assertArrayHasKey('description', $definition); - } - } - - public function testGetDefinitionsActiveReturnsOnlyActiveConstants() - { - // Mocking an active constant - $mockConstant = $this->createMock(ApbctConstant::class); - $mockConstant->method('getData')->willReturn([ - 'name' => 'APBCT_SERVICE__ACTIVE_CONSTANT', - 'description' => 'Active constant description', - 'is_defined' => true, - ]); - - $this->serviceConstants->disable_empty_email_exception = $mockConstant; - - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - - $this->assertIsArray($activeDefinitions); - $this->assertCount(1, $activeDefinitions); - $this->assertEquals('APBCT_SERVICE__ACTIVE_CONSTANT', $activeDefinitions[0]['name']); - } - - public function testGetDefinitionsActiveReturnsOnlyActiveConstantsLive() - { - define('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', true); - $this->serviceConstants = new ServiceConstants(); - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - $this->assertIsArray($activeDefinitions); - $this->assertCount(1, $activeDefinitions); - $this->assertEquals('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', $activeDefinitions[0]['is_defined']); - $this->assertEquals(true, $this->serviceConstants->disable_empty_email_exception->isDefined()); - $this->assertEquals(true, $activeDefinitions[0]['value']); - $this->assertEquals(true, $this->serviceConstants->disable_empty_email_exception->getValue()); - $this->assertNotEmpty($activeDefinitions[0]['description']); - - define('APBCT_ANTICRAWLER_EXLC_FEED', true); - $this->serviceConstants = new ServiceConstants(); - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - $this->assertIsArray($activeDefinitions); - $this->assertCount(2, $activeDefinitions); - $this->assertEquals('APBCT_ANTICRAWLER_EXLC_FEED', $activeDefinitions[1]['is_defined']); - $this->assertEquals('APBCT_ANTICRAWLER_EXLC_FEED', $this->serviceConstants->skip_anticrawler_on_rss_feed->isDefined()); - $this->assertEquals(true, $activeDefinitions[1]['value']); - $this->assertEquals(true, $this->serviceConstants->skip_anticrawler_on_rss_feed->getValue()); - $this->assertNotEmpty($activeDefinitions[1]['description']); - - define('APBCT_SET_AJAX_ROUTE_TYPE', 'admin_ajax'); - $this->serviceConstants = new ServiceConstants(); - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - $this->assertIsArray($activeDefinitions); - $this->assertCount(3, $activeDefinitions); - $this->assertEquals('APBCT_SET_AJAX_ROUTE_TYPE', $activeDefinitions[2]['is_defined']); - $this->assertEquals(true, $this->serviceConstants->set_ajax_route_type->isDefinedAndTypeOK()); - $this->assertEquals('admin_ajax', $activeDefinitions[2]['value']); - $this->assertEquals('admin_ajax', $this->serviceConstants->set_ajax_route_type->getValue()); - $this->assertNotEmpty($activeDefinitions[2]['description']); - - define('CLEANTALK_ACCESS_KEY', 'asdasdasd'); - $this->serviceConstants = new ServiceConstants(); - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - $this->assertIsArray($activeDefinitions); - $this->assertCount(4, $activeDefinitions); - $this->assertEquals('CLEANTALK_ACCESS_KEY', $activeDefinitions[3]['is_defined']); - $this->assertEquals(true, $this->serviceConstants->self_owned_access_key->isDefinedAndTypeOK()); - $this->assertEquals('asdasdasd', $activeDefinitions[3]['value']); - $this->assertEquals('asdasdasd', $this->serviceConstants->self_owned_access_key->getValue()); - $this->assertNotEmpty($activeDefinitions[3]['description']); - - define('CLEANTALK_CHECK_COMMENTS_NUMBER', false); - $this->serviceConstants = new ServiceConstants(); - $activeDefinitions = $this->serviceConstants->getDefinitionsActive(); - $this->assertIsArray($activeDefinitions); - $this->assertCount(5, $activeDefinitions); - $this->assertEquals('CLEANTALK_CHECK_COMMENTS_NUMBER', $activeDefinitions[4]['is_defined']); - $this->assertEquals(false, $this->serviceConstants->skip_on_approved_comments_number->isDefinedAndTypeOK()); - $this->assertEquals(false, $activeDefinitions[4]['value']); - $this->assertEquals(false, $this->serviceConstants->skip_on_approved_comments_number->getValue()); - $this->assertNotEmpty($activeDefinitions[4]['description']); - } -} diff --git a/tests/Inc/TestCleantalkCommon.php b/tests/Inc/TestCleantalkCommon.php index 35e856693..fb98f0ca8 100644 --- a/tests/Inc/TestCleantalkCommon.php +++ b/tests/Inc/TestCleantalkCommon.php @@ -2,8 +2,6 @@ namespace Inc; -use Cleantalk\ApbctWP\ApbctConstant; -use Cleantalk\ApbctWP\ServiceConstants; use Cleantalk\ApbctWP\State; use PHPUnit\Framework\TestCase; @@ -60,22 +58,19 @@ public function testApbctIsBotDetectorEnabledByDataTrue() $this->assertTrue($bot_detector_state); } + /** + * The service constant wins over $apbct->data. A PHP constant cannot be undefined once set, + * so each case gets its own process. + * + * @runInSeparateProcess + * @preserveGlobalState disabled + */ public function testApbctIsBotDetectorEnabledByConstant() { // Arrange global $apbct; - $apbct_constant_mock = $this->getMockBuilder(ApbctConstant::class) - ->onlyMethods(['isDefined', 'getValue']) - ->disableOriginalConstructor() - ->getMock(); - - $apbct_constant_mock->method('isDefined') - ->willReturn(true); - - $apbct_constant_mock->method('getValue') - ->willReturn(true); - $apbct->service_constants = new ServiceConstants(); - $apbct->service_constants->bot_detector_enabled = $apbct_constant_mock; + $apbct->data['bot_detector_enabled'] = 0; + define('APBCT_SERVICE__BOT_DETECTOR_ENABLED', true); // Act $bot_detector_state = apbct__is_bot_detector_enabled(); @@ -84,22 +79,16 @@ public function testApbctIsBotDetectorEnabledByConstant() $this->assertTrue($bot_detector_state); } + /** + * @runInSeparateProcess + * @preserveGlobalState disabled + */ public function testApbctIsBotDetectorDisabledByConstant() { // Arrange global $apbct; - $apbct_constant_mock = $this->getMockBuilder(ApbctConstant::class) - ->onlyMethods(['isDefined', 'getValue']) - ->disableOriginalConstructor() - ->getMock(); - - $apbct_constant_mock->method('isDefined') - ->willReturn(true); - - $apbct_constant_mock->method('getValue') - ->willReturn(false); - $apbct->service_constants = new ServiceConstants(); - $apbct->service_constants->bot_detector_enabled = $apbct_constant_mock; + $apbct->data['bot_detector_enabled'] = 1; + define('APBCT_SERVICE__BOT_DETECTOR_ENABLED', false); // Act $bot_detector_state = apbct__is_bot_detector_enabled(); From 51192277136923675fc9d8137a07d619d03dd6d6 Mon Sep 17 00:00:00 2001 From: datorik Date: Tue, 4 Aug 2026 15:44:03 +0300 Subject: [PATCH 4/8] New.Code.New constants. Test fix --- lib/Cleantalk/ApbctWP/Constant.php | 19 ++++++++----- tests/Inc/TestCleantalkCommon.php | 43 +++++------------------------- 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/lib/Cleantalk/ApbctWP/Constant.php b/lib/Cleantalk/ApbctWP/Constant.php index f8be7c4bd..480130dbb 100644 --- a/lib/Cleantalk/ApbctWP/Constant.php +++ b/lib/Cleantalk/ApbctWP/Constant.php @@ -176,7 +176,7 @@ class Constant /** * Registry cache. Built once per request by self::getRegistry(). * - * @var array[]|null + * @var array|null */ private static $registry; @@ -271,12 +271,12 @@ public static function getDefinitions() { $result = array(); - foreach ( array_keys(self::getRegistry()) as $constant ) { + foreach ( self::getRegistry() as $constant => $entry ) { $defined_name = self::getDefinedName($constant); $result[] = array( 'is_defined' => $defined_name, 'value' => $defined_name === false ? null : constant($defined_name), - 'description' => self::getRegistry()[$constant]['description'], + 'description' => $entry['description'], ); } @@ -334,8 +334,13 @@ private static function getDefinedName($constant) private static function typeIsValid($constant, $value) { $registry = self::getRegistry(); - $type = $registry[$constant]['type']; - $type = isset(self::$type_aliases[$type]) ? self::$type_aliases[$type] : $type; + + if ( ! isset($registry[$constant]) ) { + return false; + } + + $type = $registry[$constant]['type']; + $type = isset(self::$type_aliases[$type]) ? self::$type_aliases[$type] : $type; return gettype($value) === $type; } @@ -343,7 +348,7 @@ private static function typeIsValid($constant, $value) /** * Canonical name => allowed public names (canonical first, then legacy), declared type, description. * - * @return array[] + * @return array */ private static function getRegistry() { @@ -468,4 +473,4 @@ private static function getRegistry() return self::$registry; } -} \ No newline at end of file +} diff --git a/tests/Inc/TestCleantalkCommon.php b/tests/Inc/TestCleantalkCommon.php index fb98f0ca8..327808d83 100644 --- a/tests/Inc/TestCleantalkCommon.php +++ b/tests/Inc/TestCleantalkCommon.php @@ -58,42 +58,11 @@ public function testApbctIsBotDetectorEnabledByDataTrue() $this->assertTrue($bot_detector_state); } - /** - * The service constant wins over $apbct->data. A PHP constant cannot be undefined once set, - * so each case gets its own process. - * - * @runInSeparateProcess - * @preserveGlobalState disabled + /* + * The "service constant wins over $apbct->data" cases used to live here, mocking ApbctConstant + * and injecting it into the state. Constant:: is static, so there is nothing to inject, and + * defining APBCT_SERVICE__BOT_DETECTOR_ENABLED for real is not an option either: it is + * process-global and would override the data-driven cases above and in TestCleantalkPublic. + * Constant resolution itself is covered by TestConstant. */ - public function testApbctIsBotDetectorEnabledByConstant() - { - // Arrange - global $apbct; - $apbct->data['bot_detector_enabled'] = 0; - define('APBCT_SERVICE__BOT_DETECTOR_ENABLED', true); - - // Act - $bot_detector_state = apbct__is_bot_detector_enabled(); - - // Assert - $this->assertTrue($bot_detector_state); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testApbctIsBotDetectorDisabledByConstant() - { - // Arrange - global $apbct; - $apbct->data['bot_detector_enabled'] = 1; - define('APBCT_SERVICE__BOT_DETECTOR_ENABLED', false); - - // Act - $bot_detector_state = apbct__is_bot_detector_enabled(); - - // Assert - $this->assertFalse($bot_detector_state); - } } From 168158d0221cff2a06e0b31a8d3550ac2bb06ebe Mon Sep 17 00:00:00 2001 From: Aleksandr Banins Date: Tue, 4 Aug 2026 17:25:33 +0300 Subject: [PATCH 5/8] Fix comment Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/Cleantalk/ApbctWP/Constant.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/Cleantalk/ApbctWP/Constant.php b/lib/Cleantalk/ApbctWP/Constant.php index 480130dbb..e4775dd0b 100644 --- a/lib/Cleantalk/ApbctWP/Constant.php +++ b/lib/Cleantalk/ApbctWP/Constant.php @@ -3,10 +3,7 @@ namespace Cleantalk\ApbctWP; /** - * Static registry of the plugin service constants. - * - * Replaces the `$apbct->constants->->isDefinedAndTypeOK()` chain with a single static call: - * + * Replaces the `$apbct->service_constants->->isDefined()` / `getValue()` usage with a single static call: * Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE) // just defined & of the declared type * Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE, false) // ... and strictly equals false * From df8350d3679011ab4b1e7d282ca1d97eccdb8ae9 Mon Sep 17 00:00:00 2001 From: Aleksandr Banins Date: Tue, 4 Aug 2026 17:52:34 +0300 Subject: [PATCH 6/8] Code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Antispam/Integrations/CleantalkPreprocessComment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php index 063fee3a0..6fcfba0df 100644 --- a/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php +++ b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php @@ -334,7 +334,7 @@ public function doBlock($message) $err_text = '
' . ( - Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) + Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, true) ? '' : 'CleanTalk. ' ) From f279d7d79952984199dbe607ce651d10a4baa930 Mon Sep 17 00:00:00 2001 From: Aleksandr Banins Date: Tue, 4 Aug 2026 17:54:03 +0300 Subject: [PATCH 7/8] Comment fix Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/Inc/TestCleantalkCommon.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Inc/TestCleantalkCommon.php b/tests/Inc/TestCleantalkCommon.php index 327808d83..cb24bdfc4 100644 --- a/tests/Inc/TestCleantalkCommon.php +++ b/tests/Inc/TestCleantalkCommon.php @@ -63,6 +63,6 @@ public function testApbctIsBotDetectorEnabledByDataTrue() * and injecting it into the state. Constant:: is static, so there is nothing to inject, and * defining APBCT_SERVICE__BOT_DETECTOR_ENABLED for real is not an option either: it is * process-global and would override the data-driven cases above and in TestCleantalkPublic. - * Constant resolution itself is covered by TestConstant. + * Constant resolution should be covered by dedicated unit tests for Cleantalk\ApbctWP\Constant. */ } From 4a16a446c65f557790ffaa5c1c66a7aed2e79679 Mon Sep 17 00:00:00 2001 From: datorik Date: Tue, 4 Aug 2026 17:56:30 +0300 Subject: [PATCH 8/8] Code. Code review --- inc/cleantalk-public.php | 8 +- tests/ApbctWP/TestConstant.php | 150 +++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 tests/ApbctWP/TestConstant.php diff --git a/inc/cleantalk-public.php b/inc/cleantalk-public.php index 46cba54c1..8f32337c6 100644 --- a/inc/cleantalk-public.php +++ b/inc/cleantalk-public.php @@ -42,7 +42,7 @@ function apbct_init() // Localize data if ( ! apbct_exclusions_check__url() ) { - if (Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER)) { + if (Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER, true)) { add_action('wp_footer', array(LocalizeHandler::class, 'handle'), 1); add_action('login_footer', array(LocalizeHandler::class, 'handle'), 1); } else { @@ -822,7 +822,7 @@ function ct_die($_comment_id, $_comment_status) do_action('apbct_pre_block_page', $ct_comment); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) ) { + if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, true) ) { $message_title = 'CleanTalk. ' . $message_title; } if ( Post::get('et_pb_contact_email') ) { @@ -879,7 +879,7 @@ function ct_die_extended($comment_body) }, 999, 1); $message_title = __('Spam protection', 'cleantalk-spam-protect'); - if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE) ) { + if ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, true) ) { $message_title = 'CleanTalk. ' . $message_title; } @@ -1264,7 +1264,7 @@ function apbct_enqueue_and_localize_public_scripts() { global $apbct; - $in_footer = Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER); + $in_footer = Constant::is(Constant::APBCT_SERVICE__PLACE_PUBLIC_JS_SCRIPTS_IN_FOOTER, true); // Different JS params $bundle_name = ApbctJsBundleResolver::getBundleName($apbct->settings); ApbctEnqueue::getInstance()->js($bundle_name, array(), $in_footer); diff --git a/tests/ApbctWP/TestConstant.php b/tests/ApbctWP/TestConstant.php new file mode 100644 index 000000000..b2ca855e8 --- /dev/null +++ b/tests/ApbctWP/TestConstant.php @@ -0,0 +1,150 @@ +assertSame( + 'APBCT_SERVICE__DISABLE_BLOCKING_TITLE', + Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE + ); + } + + public function testDeprecatedAliasPointsToTheSameRegistryEntry() + { + $this->assertSame( + Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, + Constant::CLEANTALK_DISABLE_BLOCKING_TITLE + ); + $this->assertSame( + Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY, + Constant::CLEANTALK_ACCESS_KEY + ); + } + + public function testGetNamesListsCanonicalNameFirst() + { + $this->assertSame( + array('APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED', 'APBCT_ANTICRAWLER_EXLC_FEED'), + Constant::getNames(Constant::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED) + ); + } + + public function testGetNamesOnUnknownKeyReturnsEmptyArray() + { + $this->assertSame(array(), Constant::getNames('NO_SUCH_CONSTANT')); + } + + public function testGetDefinitionsCoversEveryRegisteredConstant() + { + $definitions = Constant::getDefinitions(); + + $this->assertNotEmpty($definitions); + foreach ($definitions as $definition) { + $this->assertSame(array('is_defined', 'value', 'description'), array_keys($definition)); + $this->assertNotEmpty($definition['description']); + } + } + + public function testNeverDefinedConstantIsFalseAndFallsBackToDefault() + { + $this->assertFalse(Constant::is(Constant::APBCT_SERVICE__WHITELABEL_ENABLED)); + $this->assertNull(Constant::getValue(Constant::APBCT_SERVICE__WHITELABEL_ENABLED)); + $this->assertSame( + 'fallback', + Constant::getValue(Constant::APBCT_SERVICE__WHITELABEL_ENABLED, 'fallback') + ); + } + + public function testResolutionAgainstLiveConstants() + { + define('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', true); + + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION)); + $this->assertTrue(Constant::getValue(Constant::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION)); + + $active = Constant::getDefinitionsActive(); + $this->assertCount(1, $active); + $this->assertSame('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', $active[0]['is_defined']); + $this->assertTrue($active[0]['value']); + $this->assertNotEmpty($active[0]['description']); + + define('APBCT_ANTICRAWLER_EXLC_FEED', true); + + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__SKIP_ANTICRAWLER_ON_RSS_FEED)); + $this->assertTrue(Constant::is(Constant::APBCT_ANTICRAWLER_EXLC_FEED)); + + $active = Constant::getDefinitionsActive(); + $this->assertCount(2, $active); + $this->assertSame('APBCT_ANTICRAWLER_EXLC_FEED', $active[1]['is_defined']); + + define('APBCT_SET_AJAX_ROUTE_TYPE', 'admin_ajax'); + + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE)); + $this->assertSame('admin_ajax', Constant::getValue(Constant::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE)); + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE, 'admin_ajax')); + $this->assertFalse(Constant::is(Constant::APBCT_SERVICE__SET_AJAX_ROUTE_TYPE, 'rest')); + + $active = Constant::getDefinitionsActive(); + $this->assertCount(3, $active); + $this->assertSame('APBCT_SET_AJAX_ROUTE_TYPE', $active[2]['is_defined']); + + define('CLEANTALK_ACCESS_KEY', 'asdasdasd'); + + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY)); + $this->assertSame('asdasdasd', Constant::getValue(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY)); + + $active = Constant::getDefinitionsActive(); + $this->assertCount(4, $active); + $this->assertSame('CLEANTALK_ACCESS_KEY', $active[3]['is_defined']); + + // --- declared int, defined as bool: rejected by the type check ------------------------- + define('CLEANTALK_CHECK_COMMENTS_NUMBER', false); + + $this->assertFalse(Constant::is(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER)); + // ... so the call site gets its default instead of a bogus value + $this->assertSame(3, Constant::getValue(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, 3)); + + // it is still reported as defined - the report shows what the site actually declared + $active = Constant::getDefinitionsActive(); + $this->assertCount(5, $active); + $this->assertSame('CLEANTALK_CHECK_COMMENTS_NUMBER', $active[4]['is_defined']); + $this->assertFalse($active[4]['value']); + $this->assertNotEmpty($active[4]['description']); + } + + /** + * gettype() answers 'boolean'/'integer' while the registry declares 'bool'/'int'. Without the + * alias map no bool or int constant would ever validate - which is how the previous + * implementation silently disabled CLEANTALK_CHECK_COMMENTS_NUMBER. + * + * @depends testResolutionAgainstLiveConstants + */ + public function testDeclaredBoolTypeIsAcceptedDespiteGettypeNaming() + { + $this->assertSame('boolean', gettype(constant('APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION'))); + $this->assertTrue(Constant::is(Constant::APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION)); + } + + /** + * The registry caches metadata, never values - a constant defined after the first lookup is + * still picked up. + * + * @depends testResolutionAgainstLiveConstants + */ + public function testRegistryCachesMetadataNotValues() + { + $this->assertSame( + array( + 'APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER', + 'CLEANTALK_CHECK_COMMENTS_NUMBER', + ), + Constant::getNames(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER) + ); + // CLEANTALK_CHECK_COMMENTS_NUMBER was defined after the registry had already been built + $this->assertNotEmpty(Constant::getDefinitionsActive()); + } +}