prefix = $prefix; $this->window = max( 1, $window ); $this->max = max( 1, $max ); } public function consume(): bool { $ip = $this->clientIp(); if ( $ip === '' ) { return true; } $key = $this->prefix . \wp_hash( $ip ); $bucket = \get_transient( $key ); $count = is_array( $bucket ) && isset( $bucket['count'] ) ? (int) $bucket['count'] : 0; if ( $count >= $this->max ) { return false; } \set_transient( $key, [ 'count' => $count + 1 ], $this->window ); return true; } private function clientIp(): string { if ( empty( $_SERVER['REMOTE_ADDR'] ) ) { return ''; } $remote = \sanitize_text_field( \wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) ); $remote = \filter_var( $remote, FILTER_VALIDATE_IP ); if ( $remote === false ) { return ''; } if ( ! $this->isTrustedProxy( $remote ) ) { return $remote; } return $this->forwardedHeaderIp() ?: $remote; } private function forwardedHeaderIp(): string { $headers = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR' ]; foreach ( $headers as $key ) { if ( empty( $_SERVER[ $key ] ) ) { continue; } $value = \sanitize_text_field( \wp_unslash( (string) $_SERVER[ $key ] ) ); if ( strpos( $value, ',' ) !== false ) { $value = trim( explode( ',', $value )[0] ); } $valid = \filter_var( $value, FILTER_VALIDATE_IP ); if ( $valid !== false ) { return $valid; } } return ''; } private function isTrustedProxy( string $remote ): bool { $proxies = \apply_filters( 'agent001_trusted_proxies', [] ); if ( ! is_array( $proxies ) || $proxies === [] ) { return false; } foreach ( $proxies as $candidate ) { if ( ! is_string( $candidate ) || $candidate === '' ) { continue; } if ( $this->ipMatches( $remote, $candidate ) ) { return true; } } return false; } private function ipMatches( string $ip, string $rangeOrIp ): bool { if ( strpos( $rangeOrIp, '/' ) === false ) { return $ip === $rangeOrIp; } [ $subnet, $bits ] = explode( '/', $rangeOrIp, 2 ); $bitsInt = (int) $bits; if ( ! \filter_var( $subnet, FILTER_VALIDATE_IP ) ) { return false; } $ipPacked = @\inet_pton( $ip ); $subnetPacked = @\inet_pton( $subnet ); if ( $ipPacked === false || $subnetPacked === false ) { return false; } if ( strlen( $ipPacked ) !== strlen( $subnetPacked ) ) { return false; } $byteLen = strlen( $ipPacked ); $totalBits = $byteLen * 8; if ( $bitsInt < 0 || $bitsInt > $totalBits ) { return false; } if ( $bitsInt === 0 ) { return true; } $fullBytes = intdiv( $bitsInt, 8 ); $remainder = $bitsInt % 8; if ( $fullBytes > 0 && substr( $ipPacked, 0, $fullBytes ) !== substr( $subnetPacked, 0, $fullBytes ) ) { return false; } if ( $remainder === 0 ) { return true; } $mask = chr( ( 0xFF << ( 8 - $remainder ) ) & 0xFF ); return ( $ipPacked[ $fullBytes ] & $mask ) === ( $subnetPacked[ $fullBytes ] & $mask ); } }