diff --git a/cleantalk.php b/cleantalk.php index c1eb6c4bb..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; @@ -56,7 +57,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 +141,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 +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 && defined('CLEANTALK_ACCESS_KEY') ? $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' ) { @@ -1253,8 +1266,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 (Constant::is(Constant::APBCT_SERVICE__SFW_FORCE_DIRECT_UPDATE)) { + $apbct->fw_stats['reason_direct_update_log'] = 'constant exists'; return true; } @@ -2808,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 (defined('APBCT_WHITELABEL_PLUGIN_DESCRIPTION')) { + if (Constant::is(Constant::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION)) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_antispam_description'] = APBCT_WHITELABEL_PLUGIN_DESCRIPTION; + $result['wl_antispam_description'] = esc_html(Constant::getValue(Constant::APBCT_SERVICE__WHITELABEL_PLUGIN_DESCRIPTION)); } //todo:temporary solution for FAQ - if (defined('APBCT_WHITELABEL_FAQ_LINK')) { + if (Constant::is(Constant::APBCT_SERVICE__WHITELABEL_FAQ_LINK)) { /** @psalm-suppress PossiblyInvalidArrayAssignment */ - $result['wl_faq_url'] = APBCT_WHITELABEL_FAQ_LINK; + $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 54cf6d4ea..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->service_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 (defined('APBCT_DO_NOT_COLLECT_FRONTEND_DATA_LOGS')) { + 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->service_constants->bot_detector_enabled) && $apbct->service_constants->bot_detector_enabled->isDefined() ) { - return (bool) $apbct->service_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 d6c11b6b5..218b0dc90 100644 --- a/inc/cleantalk-pluggable.php +++ b/inc/cleantalk-pluggable.php @@ -1,6 +1,7 @@ service_constants->set_ajax_route_type->isDefined() - && in_array($apbct->service_constants->set_ajax_route_type->getValue(), array('rest','admin_ajax')) - ) { - return $apbct->service_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 007f6983e..8f32337c6 100644 --- a/inc/cleantalk-public.php +++ b/inc/cleantalk-public.php @@ -1,6 +1,7 @@ CleanTalk. ' . $message_title; } if ( Post::get('et_pb_contact_email') ) { @@ -878,7 +879,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 ( ! Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, true) ) { $message_title = 'CleanTalk. ' . $message_title; } @@ -1263,7 +1264,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 = 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/inc/cleantalk-settings.php b/inc/cleantalk-settings.php index 8aacc9a3a..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,7 +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'), - defined('CLEANTALK_CHECK_COMMENTS_NUMBER') ? CLEANTALK_CHECK_COMMENTS_NUMBER : 3 + Constant::getValue(Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, 3) ), ), 'data__use_ajax' => array( @@ -1148,7 +1149,7 @@ function apbct_settings__set_fields__network($fields) '' ), 'childrens' => array('multisite__white_label__plugin_name'), - 'disabled' => defined('CLEANTALK_ACCESS_KEY') || + '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', @@ -1757,7 +1758,7 @@ function apbct_settings__error__output($return = false) $out = ''; - if ( ! empty($apbct->errors) && ! defined('CLEANTALK_ACCESS_KEY') ) { + if ( ! empty($apbct->errors) && ! Constant::is(Constant::APBCT_SERVICE__SELF_OWNED_ACCESS_KEY) ) { $errors = $apbct->errors; $error_texts = array( @@ -2051,7 +2052,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 || + 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"]); @@ -2424,8 +2428,10 @@ function apbct_settings__validate($incoming_settings) $apbct->data['key_changed'] = $incoming_settings['apikey'] !== $apbct->settings['apikey']; + $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'] = 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/Antispam/Integrations/CleantalkPreprocessComment.php b/lib/Cleantalk/Antispam/Integrations/CleantalkPreprocessComment.php index 0f0f9699a..6fcfba0df 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,7 +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 = defined('CLEANTALK_CHECK_COMMENTS_NUMBER') ? CLEANTALK_CHECK_COMMENTS_NUMBER : 3; + $this->comments_check_number_needs_to_skip_request = Constant::getValue( + Constant::APBCT_SERVICE__SKIP_ON_APPROVED_COMMENTS_NUMBER, + 3 + ); /** * Custom mail notifications processing @@ -329,9 +333,11 @@ public function doBlock($message) $err_text = '
' - . ((defined('CLEANTALK_DISABLE_BLOCKING_TITLE') && CLEANTALK_DISABLE_BLOCKING_TITLE == true) - ? '' - : 'CleanTalk. ') + . ( + Constant::is(Constant::APBCT_SERVICE__DISABLE_BLOCKING_TITLE, true) + ? '' + : '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..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,7 +79,7 @@ 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 = 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 d546fc4ec..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 && ! defined('CLEANTALK_ACCESS_KEY') ) { + 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 536deb799..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 && ! defined('CLEANTALK_ACCESS_KEY'); + $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 10cd8ac28..000000000 --- a/lib/Cleantalk/ApbctWP/ApbctConstant.php +++ /dev/null @@ -1,67 +0,0 @@ -allowed_public_names = $allowed_public_names; - $this->description = $description; - } - - /** - * 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 - */ - public function isDefined() - { - foreach ($this->allowed_public_names as $name) { - if (defined($name)) { - return $name; - } - } - return false; - } - - /** - * Returns the value of the first defined constant from the allowed names. Return null if none of the constants are defined. - * - * @return string|null - */ - public function getValue() - { - foreach ($this->allowed_public_names as $name) { - if (defined($name)) { - return (string)constant($name); - } - } - return null; - } - - /** - * @return array - */ - public function getData() - { - return array( - 'is_defined' => $this->isDefined(), - '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..e4775dd0b --- /dev/null +++ b/lib/Cleantalk/ApbctWP/Constant.php @@ -0,0 +1,473 @@ +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 + * + * 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 ( 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' => $entry['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(); + + 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; + } + + /** + * 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; + } +} diff --git a/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php b/lib/Cleantalk/ApbctWP/Firewall/AntiCrawler.php index 7c316b841..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->service_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->service_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/Localize/CtPublicFunctionsLocalize.php b/lib/Cleantalk/ApbctWP/Localize/CtPublicFunctionsLocalize.php index c1bbcdcaf..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,7 +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__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/RemoteCalls.php b/lib/Cleantalk/ApbctWP/RemoteCalls.php index 2ebd69466..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->service_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 84b4b4b77..000000000 --- a/lib/Cleantalk/ApbctWP/ServiceConstants.php +++ /dev/null @@ -1,135 +0,0 @@ -disable_empty_email_exception = new ApbctConstant( - array( - 'APBCT_SERVICE__DISABLE_EMPTY_EMAIL_EXCEPTION', - ), - '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', - ), - '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', - ), - '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.', -// ), -// ); - } - - /** - * 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 af42df288..e0d637726 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 @@ -398,10 +397,6 @@ class State extends \Cleantalk\Common\State * @var LoginIPKeeper */ public $login_ip_keeper; - /** - * @var ServiceConstants - */ - public $service_constants; private $auto_save_defaults_list = array(); @@ -548,8 +543,6 @@ protected function setDefinitions() // Limit for firewall logs sending. define('APBCT_SFW_SEND_LOGS_LIMIT', 1000); } - - $this->service_constants = new ServiceConstants(); } protected function setOptions() 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 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()); + } +} diff --git a/tests/ApbctWP/TestServiceConstants.php b/tests/ApbctWP/TestServiceConstants.php deleted file mode 100644 index 65467075a..000000000 --- a/tests/ApbctWP/TestServiceConstants.php +++ /dev/null @@ -1,83 +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('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->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('1', $activeDefinitions[1]['value']); - $this->assertEquals('1', $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('APBCT_SET_AJAX_ROUTE_TYPE', $this->serviceConstants->set_ajax_route_type->isDefined()); - $this->assertEquals('admin_ajax', $activeDefinitions[2]['value']); - $this->assertEquals('admin_ajax', $this->serviceConstants->set_ajax_route_type->getValue()); - $this->assertNotEmpty($activeDefinitions[2]['description']); - } -} diff --git a/tests/Inc/TestCleantalkCommon.php b/tests/Inc/TestCleantalkCommon.php index 35e856693..cb24bdfc4 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,51 +58,11 @@ public function testApbctIsBotDetectorEnabledByDataTrue() $this->assertTrue($bot_detector_state); } - 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; - - // Act - $bot_detector_state = apbct__is_bot_detector_enabled(); - - // Assert - $this->assertTrue($bot_detector_state); - } - - 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; - - // Act - $bot_detector_state = apbct__is_bot_detector_enabled(); - - // Assert - $this->assertFalse($bot_detector_state); - } + /* + * 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 should be covered by dedicated unit tests for Cleantalk\ApbctWP\Constant. + */ }