PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / src / Service / OptInValidator.php

OptInValidator.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at src/Service/OptInValidator.php

186 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OptIn Validator Service
4 *
5 * @package Forge12\DoubleOptIn\Service
6 * @since 4.0.0
7 */
8
9 namespace Forge12\DoubleOptIn\Service;
10
11 use Forge12\DoubleOptIn\Entity\OptIn;
12 use Forge12\Shared\LoggerInterface;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 /**
19 * Class OptInValidator
20 *
21 * Validates OptIn data before persistence.
22 */
23 class OptInValidator {
24
25 private LoggerInterface $logger;
26
27 /**
28 * Validation errors from last validate() call.
29 *
30 * @var array<string, string>
31 */
32 private array $errors = array();
33
34 /**
35 * Constructor.
36 *
37 * @param LoggerInterface $logger The logger.
38 */
39 public function __construct( LoggerInterface $logger ) {
40 $this->logger = $logger;
41 }
42
43 /**
44 * Validate an OptIn entity.
45 *
46 * @param OptIn $optIn The entity to validate.
47 *
48 * @return bool True if valid.
49 */
50 public function validate( OptIn $optIn ): bool {
51 $this->errors = array();
52
53 $this->validateEmail( $optIn );
54 $this->validateFormId( $optIn );
55 $this->validateContent( $optIn );
56
57 if ( ! empty( $this->errors ) ) {
58 $this->logger->warning(
59 'OptIn validation failed',
60 array(
61 'plugin' => 'double-opt-in',
62 'errors' => $this->errors,
63 )
64 );
65 return false;
66 }
67
68 return true;
69 }
70
71 /**
72 * Get validation errors from last validate() call.
73 *
74 * @return array<string, string>
75 */
76 public function getErrors(): array {
77 return $this->errors;
78 }
79
80 /**
81 * Validate email address.
82 *
83 * @param OptIn $optIn The entity.
84 */
85 private function validateEmail( OptIn $optIn ): void {
86 $email = $optIn->getEmail();
87
88 if ( empty( $email ) ) {
89 $this->errors['email'] = __( 'Email address is required.', 'double-opt-in' );
90 return;
91 }
92
93 if ( ! is_email( $email ) ) {
94 $this->errors['email'] = __( 'Invalid email address format.', 'double-opt-in' );
95 }
96 }
97
98 /**
99 * Validate form ID.
100 *
101 * @param OptIn $optIn The entity.
102 */
103 private function validateFormId( OptIn $optIn ): void {
104 if ( $optIn->getFormId() <= 0 ) {
105 $this->errors['formId'] = __( 'Form ID is required.', 'double-opt-in' );
106 }
107 }
108
109 /**
110 * Validate content encoding.
111 *
112 * @param OptIn $optIn The entity.
113 */
114 private function validateContent( OptIn $optIn ): void {
115 $content = $optIn->getContent();
116
117 if ( ! empty( $content ) && ! $this->isValidUtf8( $content ) ) {
118 $this->errors['content'] = __( 'Content contains invalid characters.', 'double-opt-in' );
119 }
120 }
121
122 /**
123 * Check if string is valid UTF-8.
124 *
125 * @param string $string The string to check.
126 *
127 * @return bool
128 */
129 private function isValidUtf8( string $string ): bool {
130 return mb_check_encoding( $string, 'UTF-8' );
131 }
132
133 /**
134 * Validate hash format.
135 *
136 * @param string $hash The hash to validate.
137 *
138 * @return bool
139 */
140 public function isValidHash( string $hash ): bool {
141 if ( empty( $hash ) ) {
142 return false;
143 }
144
145 // Hash should be base64 encoded
146 $decoded = base64_decode( $hash, true );
147
148 return $decoded !== false && strlen( $decoded ) > 0;
149 }
150
151 /**
152 * Validate IP address format.
153 *
154 * @param string $ip The IP to validate.
155 *
156 * @return bool
157 */
158 public function isValidIp( string $ip ): bool {
159 return filter_var( $ip, FILTER_VALIDATE_IP ) !== false;
160 }
161
162 /**
163 * Check if content size is within database limits.
164 *
165 * @param string $content The content to check.
166 *
167 * @return bool
168 */
169 public function isWithinSizeLimit( string $content ): bool {
170 global $wpdb;
171
172 // Get max_allowed_packet if available
173 $maxPacket = $wpdb->get_var( "SHOW VARIABLES LIKE 'max_allowed_packet'" );
174
175 if ( $maxPacket === null ) {
176 // Default to 1MB if we can't determine
177 $maxPacket = 1048576;
178 }
179
180 // Leave some margin (80% of max)
181 $limit = (int) $maxPacket * 0.8;
182
183 return strlen( $content ) <= $limit;
184 }
185 }
186