*/ private $settings; /** * Constructor. * * @param array $settings Raw settings. */ private function __construct( array $settings ) { $this->settings = $this->sanitize( $settings ); } /** * Create from a settings array. * * @param array $settings Raw settings. */ public static function from_array( array $settings ): self { return new self( $settings ); } /** * Get default engine settings. * * @return array */ public static function defaults(): array { return array( 'protection_mode' => self::MODE_PROTECT, 'strictness' => self::STRICTNESS_BALANCED, 'minimum_fill_time_seconds' => 3, 'token_lifetime_seconds' => 1800, 'rate_limit_window_seconds' => 300, 'rate_limit_threshold' => 5, 'link_density_threshold' => 0.5, 'honeypot_check_enabled' => true, 'minimum_time_check_enabled' => true, 'javascript_token_check_enabled' => true, 'rate_limit_check_enabled' => true, 'link_density_check_enabled' => true, ); } /** * Get protection mode. */ public function get_protection_mode(): string { return (string) $this->settings['protection_mode']; } /** * Get strictness. */ public function get_strictness(): string { return (string) $this->settings['strictness']; } /** * Get minimum fill time. */ public function get_minimum_fill_time_seconds(): int { return (int) $this->settings['minimum_fill_time_seconds']; } /** * Get token lifetime. */ public function get_token_lifetime_seconds(): int { return (int) $this->settings['token_lifetime_seconds']; } /** * Get rate-limit window. */ public function get_rate_limit_window_seconds(): int { return (int) $this->settings['rate_limit_window_seconds']; } /** * Get rate-limit threshold. */ public function get_rate_limit_threshold(): int { return (int) $this->settings['rate_limit_threshold']; } /** * Get link-density threshold. */ public function get_link_density_threshold(): float { return (float) $this->settings['link_density_threshold']; } /** * Check whether a named check is enabled. * * @param string $key Setting key. */ public function is_check_enabled( string $key ): bool { return ! empty( $this->settings[ $key ] ); } /** * Get settings as an array. * * @return array */ public function to_array(): array { return $this->settings; } /** * Sanitize engine settings. * * @param array $settings Raw settings. * * @return array */ private function sanitize( array $settings ): array { $defaults = self::defaults(); $sanitized = $defaults; if ( isset( $settings['protection_mode'] ) && is_scalar( $settings['protection_mode'] ) ) { $mode = strtolower( (string) $settings['protection_mode'] ); if ( in_array( $mode, array( self::MODE_PROTECT, self::MODE_MONITOR, self::MODE_DISABLED ), true ) ) { $sanitized['protection_mode'] = $mode; } } if ( isset( $settings['strictness'] ) && is_scalar( $settings['strictness'] ) ) { $strictness = strtolower( (string) $settings['strictness'] ); if ( in_array( $strictness, array( self::STRICTNESS_SAFE, self::STRICTNESS_BALANCED, self::STRICTNESS_STRICT ), true ) ) { $sanitized['strictness'] = $strictness; } } $sanitized['minimum_fill_time_seconds'] = $this->bounded_int( $settings, 'minimum_fill_time_seconds', 1, 60, (int) $defaults['minimum_fill_time_seconds'] ); $sanitized['token_lifetime_seconds'] = $this->bounded_int( $settings, 'token_lifetime_seconds', 60, 86400, (int) $defaults['token_lifetime_seconds'] ); $sanitized['rate_limit_window_seconds'] = $this->bounded_int( $settings, 'rate_limit_window_seconds', 60, 86400, (int) $defaults['rate_limit_window_seconds'] ); $sanitized['rate_limit_threshold'] = $this->bounded_int( $settings, 'rate_limit_threshold', 1, 1000, (int) $defaults['rate_limit_threshold'] ); $sanitized['link_density_threshold'] = $this->bounded_float( $settings, 'link_density_threshold', 0.05, 1.0, (float) $defaults['link_density_threshold'] ); foreach ( array( 'honeypot_check_enabled', 'minimum_time_check_enabled', 'javascript_token_check_enabled', 'rate_limit_check_enabled', 'link_density_check_enabled' ) as $enabled_key ) { if ( array_key_exists( $enabled_key, $settings ) ) { $sanitized[ $enabled_key ] = $this->to_bool( $settings[ $enabled_key ] ); } } return $sanitized; } /** * Get a bounded integer from settings. * * @param array $settings Raw settings. * @param string $key Setting key. * @param int $min Minimum value. * @param int $max Maximum value. * @param int $default_value Default value. */ private function bounded_int( array $settings, string $key, int $min, int $max, int $default_value ): int { if ( ! isset( $settings[ $key ] ) || ! is_scalar( $settings[ $key ] ) ) { return $default_value; } $value = (int) $settings[ $key ]; if ( $value < $min ) { return $min; } if ( $value > $max ) { return $max; } return $value; } /** * Get a bounded float from settings. * * @param array $settings Raw settings. * @param string $key Setting key. * @param float $min Minimum value. * @param float $max Maximum value. * @param float $default_value Default value. */ private function bounded_float( array $settings, string $key, float $min, float $max, float $default_value ): float { if ( ! isset( $settings[ $key ] ) || ! is_scalar( $settings[ $key ] ) ) { return $default_value; } $value = (float) $settings[ $key ]; if ( $value < $min ) { return $min; } if ( $value > $max ) { return $max; } return $value; } /** * Convert a value to a boolean. * * @param mixed $value Raw value. */ private function to_bool( $value ): bool { if ( is_bool( $value ) ) { return $value; } if ( ! is_scalar( $value ) ) { return false; } return in_array( strtolower( (string) $value ), array( '1', 'true', 'yes', 'on' ), true ); } }