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 / Entity / OptIn.php

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

568 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OptIn Entity
4 *
5 * Pure data object with no database logic or side effects.
6 *
7 * @package Forge12\DoubleOptIn\Entity
8 * @since 4.0.0
9 */
10
11 namespace Forge12\DoubleOptIn\Entity;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class OptIn
19 *
20 * Immutable-style entity representing an opt-in record.
21 * Use withX() methods to create modified copies.
22 */
23 class OptIn {
24
25 private int $id = 0;
26 private int $formId = 0;
27 private bool $confirmed = false;
28 private string $content = '';
29 private string $hash = '';
30 private int $createTime = 0;
31 private int $updateTime = 0;
32 private int $optOutTime = 0;
33 private string $ipRegister = '';
34 private string $ipConfirmation = '';
35 private string $ipOptOut = '';
36 private string $files = '';
37 private int $category = 0;
38 private string $form = '';
39 private string $mailOptIn = '';
40 private string $email = '';
41 private string $consentText = '';
42 private string $consentField = '';
43 private int $reminderSentAt = 0;
44 private string $mailReminder = '';
45
46 /**
47 * Private constructor - use static factory methods.
48 */
49 private function __construct() {
50 }
51
52 /**
53 * Parse a timestamp value from the database.
54 *
55 * Supports both Unix timestamps (numeric strings) and ISO 8601 datetime strings.
56 *
57 * @param mixed $value The timestamp value from the database.
58 *
59 * @return int The Unix timestamp.
60 */
61 private static function parseTimestamp( $value ): int {
62 if ( empty( $value ) || $value === '0' ) {
63 return 0;
64 }
65 if ( is_numeric( $value ) ) {
66 return (int) $value;
67 }
68 $ts = strtotime( $value );
69 return $ts !== false ? $ts : 0;
70 }
71
72 /**
73 * Create an OptIn entity from a database row array.
74 *
75 * @param array $data The database row.
76 *
77 * @return self
78 */
79 public static function fromArray( array $data ): self {
80 $optIn = new self();
81
82 $optIn->id = (int) ( $data['id'] ?? 0 );
83 $optIn->formId = (int) ( $data['cf_form_id'] ?? 0 );
84 $optIn->confirmed = (bool) ( $data['doubleoptin'] ?? false );
85 $optIn->content = (string) ( $data['content'] ?? '' );
86 $optIn->hash = (string) ( $data['hash'] ?? '' );
87 $optIn->createTime = self::parseTimestamp( $data['createtime'] ?? 0 );
88 $optIn->updateTime = self::parseTimestamp( $data['updatetime'] ?? 0 );
89 $optIn->optOutTime = self::parseTimestamp( $data['optouttime'] ?? 0 );
90 $optIn->ipRegister = (string) ( $data['ipaddr_register'] ?? '' );
91 $optIn->ipConfirmation = (string) ( $data['ipaddr_confirmation'] ?? '' );
92 $optIn->ipOptOut = (string) ( $data['ipaddr_optout'] ?? '' );
93 $optIn->files = (string) ( $data['files'] ?? '' );
94 $optIn->category = (int) ( $data['category'] ?? 0 );
95 $optIn->form = (string) ( $data['form'] ?? '' );
96 $optIn->mailOptIn = (string) ( $data['mail_optin'] ?? '' );
97 $optIn->email = (string) ( $data['email'] ?? '' );
98 $optIn->consentText = (string) ( $data['consent_text'] ?? '' );
99 $optIn->consentField = (string) ( $data['consent_field'] ?? '' );
100 $optIn->reminderSentAt = self::parseTimestamp( $data['reminder_sent_at'] ?? 0 );
101 $optIn->mailReminder = (string) ( $data['mail_reminder'] ?? '' );
102
103 return $optIn;
104 }
105
106 /**
107 * Create a new empty OptIn entity.
108 *
109 * @return self
110 */
111 public static function create(): self {
112 $optIn = new self();
113 $optIn->createTime = time();
114 return $optIn;
115 }
116
117 /**
118 * Convert entity to database array format.
119 *
120 * @return array
121 */
122 public function toArray(): array {
123 return array(
124 'id' => $this->id,
125 'cf_form_id' => $this->formId,
126 'doubleoptin' => (int) $this->confirmed,
127 'content' => $this->content,
128 'hash' => $this->hash,
129 'createtime' => $this->createTime > 0 ? gmdate( 'Y-m-d H:i:s', $this->createTime ) : '',
130 'updatetime' => $this->updateTime > 0 ? gmdate( 'Y-m-d H:i:s', $this->updateTime ) : '',
131 'optouttime' => $this->optOutTime > 0 ? gmdate( 'Y-m-d H:i:s', $this->optOutTime ) : '',
132 'ipaddr_register' => $this->ipRegister,
133 'ipaddr_confirmation' => $this->ipConfirmation,
134 'ipaddr_optout' => $this->ipOptOut,
135 'files' => $this->files,
136 'category' => $this->category,
137 'form' => $this->form,
138 'mail_optin' => $this->mailOptIn,
139 'email' => $this->email,
140 'consent_text' => $this->consentText,
141 'consent_field' => $this->consentField,
142 'reminder_sent_at' => $this->reminderSentAt > 0 ? gmdate( 'Y-m-d H:i:s', $this->reminderSentAt ) : '',
143 'mail_reminder' => $this->mailReminder,
144 );
145 }
146
147 /**
148 * Convert to array for database insert (excludes id).
149 *
150 * @return array
151 */
152 public function toInsertArray(): array {
153 $data = $this->toArray();
154 unset( $data['id'] );
155 return $data;
156 }
157
158 // =========================================================================
159 // GETTERS
160 // =========================================================================
161
162 public function getId(): int {
163 return $this->id;
164 }
165
166 public function getFormId(): int {
167 return $this->formId;
168 }
169
170 public function isConfirmed(): bool {
171 return $this->confirmed;
172 }
173
174 public function getContent(): string {
175 return $this->content;
176 }
177
178 /**
179 * Get content as unserialized array.
180 *
181 * @return array
182 */
183 public function getContentArray(): array {
184 $data = maybe_unserialize( $this->content );
185 return is_array( $data ) ? $data : array();
186 }
187
188 public function getHash(): string {
189 return $this->hash;
190 }
191
192 public function getCreateTime(): int {
193 return $this->createTime;
194 }
195
196 /**
197 * Get formatted create time.
198 *
199 * @param string $format The date format (default: WordPress setting).
200 *
201 * @return string
202 */
203 public function getCreateTimeFormatted( string $format = '' ): string {
204 if ( empty( $format ) ) {
205 $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
206 }
207 return $this->createTime > 0
208 ? wp_date( $format, $this->createTime )
209 : '';
210 }
211
212 public function getUpdateTime(): int {
213 return $this->updateTime;
214 }
215
216 /**
217 * Get formatted update time.
218 *
219 * @param string $format The date format.
220 *
221 * @return string
222 */
223 public function getUpdateTimeFormatted( string $format = '' ): string {
224 if ( empty( $format ) ) {
225 $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
226 }
227 return $this->updateTime > 0
228 ? wp_date( $format, $this->updateTime )
229 : '';
230 }
231
232 public function getOptOutTime(): int {
233 return $this->optOutTime;
234 }
235
236 /**
237 * Get create time as ISO 8601 string.
238 *
239 * @return string
240 */
241 public function getCreateTimeISO(): string {
242 return $this->createTime > 0 ? gmdate( 'c', $this->createTime ) : '';
243 }
244
245 /**
246 * Get update time as ISO 8601 string.
247 *
248 * @return string
249 */
250 public function getUpdateTimeISO(): string {
251 return $this->updateTime > 0 ? gmdate( 'c', $this->updateTime ) : '';
252 }
253
254 /**
255 * Get opt-out time as ISO 8601 string.
256 *
257 * @return string
258 */
259 public function getOptOutTimeISO(): string {
260 return $this->optOutTime > 0 ? gmdate( 'c', $this->optOutTime ) : '';
261 }
262
263 public function getIpRegister(): string {
264 return $this->ipRegister;
265 }
266
267 public function getIpConfirmation(): string {
268 return $this->ipConfirmation;
269 }
270
271 public function getIpOptOut(): string {
272 return $this->ipOptOut;
273 }
274
275 public function getFiles(): string {
276 return $this->files;
277 }
278
279 /**
280 * Get files as unserialized array.
281 *
282 * @return array
283 */
284 public function getFilesArray(): array {
285 $data = maybe_unserialize( $this->files );
286 return is_array( $data ) ? $data : array();
287 }
288
289 public function getCategory(): int {
290 return $this->category;
291 }
292
293 public function getForm(): string {
294 return $this->form;
295 }
296
297 public function getMailOptIn(): string {
298 return $this->mailOptIn;
299 }
300
301 public function getEmail(): string {
302 return $this->email;
303 }
304
305 public function getConsentText(): string {
306 return $this->consentText;
307 }
308
309 public function getConsentField(): string {
310 return $this->consentField;
311 }
312
313 public function getReminderSentAt(): int {
314 return $this->reminderSentAt;
315 }
316
317 /**
318 * Get formatted reminder sent time.
319 *
320 * @param string $format The date format (default: WordPress setting).
321 *
322 * @return string
323 */
324 public function getReminderSentAtFormatted( string $format = '' ): string {
325 if ( empty( $format ) ) {
326 $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
327 }
328 return $this->reminderSentAt > 0
329 ? wp_date( $format, $this->reminderSentAt )
330 : '';
331 }
332
333 /**
334 * Get reminder sent time as ISO 8601 string.
335 *
336 * @return string
337 */
338 public function getReminderSentAtISO(): string {
339 return $this->reminderSentAt > 0 ? gmdate( 'c', $this->reminderSentAt ) : '';
340 }
341
342 public function getMailReminder(): string {
343 return $this->mailReminder;
344 }
345
346 /**
347 * Check if a reminder has already been sent.
348 *
349 * @return bool
350 */
351 public function hasReminderBeenSent(): bool {
352 return $this->reminderSentAt > 0;
353 }
354
355 // =========================================================================
356 // BUSINESS LOGIC
357 // =========================================================================
358
359 /**
360 * Check if this opt-in has been opted out.
361 *
362 * @return bool
363 */
364 public function isOptedOut(): bool {
365 return ! $this->confirmed
366 && ! empty( $this->ipOptOut )
367 && $this->optOutTime > 0;
368 }
369
370 /**
371 * Check if this is a new (unsaved) entity.
372 *
373 * @return bool
374 */
375 public function isNew(): bool {
376 return $this->id === 0;
377 }
378
379 // =========================================================================
380 // IMMUTABLE SETTERS (return new instance)
381 // =========================================================================
382
383 public function withId( int $id ): self {
384 $clone = clone $this;
385 $clone->id = $id;
386 return $clone;
387 }
388
389 public function withFormId( int $formId ): self {
390 $clone = clone $this;
391 $clone->formId = $formId;
392 return $clone;
393 }
394
395 public function withConfirmed( bool $confirmed ): self {
396 $clone = clone $this;
397 $clone->confirmed = $confirmed;
398 return $clone;
399 }
400
401 public function withContent( string $content ): self {
402 $clone = clone $this;
403 $clone->content = $content;
404 return $clone;
405 }
406
407 /**
408 * Set content from array (will be serialized).
409 *
410 * @param array $data The data to serialize.
411 *
412 * @return self
413 */
414 public function withContentArray( array $data ): self {
415 $clone = clone $this;
416 $clone->content = maybe_serialize( $data );
417 return $clone;
418 }
419
420 public function withHash( string $hash ): self {
421 $clone = clone $this;
422 $clone->hash = $hash;
423 return $clone;
424 }
425
426 public function withCreateTime( int $time ): self {
427 $clone = clone $this;
428 $clone->createTime = $time;
429 return $clone;
430 }
431
432 public function withUpdateTime( int $time ): self {
433 $clone = clone $this;
434 $clone->updateTime = $time;
435 return $clone;
436 }
437
438 public function withOptOutTime( int $time ): self {
439 $clone = clone $this;
440 $clone->optOutTime = $time;
441 return $clone;
442 }
443
444 public function withIpRegister( string $ip ): self {
445 $clone = clone $this;
446 $clone->ipRegister = $ip;
447 return $clone;
448 }
449
450 public function withIpConfirmation( string $ip ): self {
451 $clone = clone $this;
452 $clone->ipConfirmation = $ip;
453 return $clone;
454 }
455
456 public function withIpOptOut( string $ip ): self {
457 $clone = clone $this;
458 $clone->ipOptOut = $ip;
459 return $clone;
460 }
461
462 public function withFiles( string $files ): self {
463 $clone = clone $this;
464 $clone->files = $files;
465 return $clone;
466 }
467
468 /**
469 * Set files from array (will be serialized).
470 *
471 * @param array $files The files array.
472 *
473 * @return self
474 */
475 public function withFilesArray( array $files ): self {
476 $clone = clone $this;
477 $clone->files = maybe_serialize( $files );
478 return $clone;
479 }
480
481 public function withCategory( int $category ): self {
482 $clone = clone $this;
483 $clone->category = $category;
484 return $clone;
485 }
486
487 public function withForm( string $form ): self {
488 $clone = clone $this;
489 $clone->form = $form;
490 return $clone;
491 }
492
493 public function withMailOptIn( string $mail ): self {
494 $clone = clone $this;
495 $clone->mailOptIn = $mail;
496 return $clone;
497 }
498
499 public function withEmail( string $email ): self {
500 $clone = clone $this;
501 $clone->email = $email;
502 return $clone;
503 }
504
505 public function withConsentText( string $consentText ): self {
506 $clone = clone $this;
507 $clone->consentText = $consentText;
508 return $clone;
509 }
510
511 public function withConsentField( string $consentField ): self {
512 $clone = clone $this;
513 $clone->consentField = $consentField;
514 return $clone;
515 }
516
517 public function withReminderSentAt( int $time ): self {
518 $clone = clone $this;
519 $clone->reminderSentAt = $time;
520 return $clone;
521 }
522
523 public function withMailReminder( string $mail ): self {
524 $clone = clone $this;
525 $clone->mailReminder = $mail;
526 return $clone;
527 }
528
529 // =========================================================================
530 // MUTABLE SETTERS (for BC compatibility)
531 // =========================================================================
532
533 /**
534 * @internal Use withX() methods for new code.
535 */
536 public function setId( int $id ): void {
537 $this->id = $id;
538 }
539
540 /**
541 * @internal Use withX() methods for new code.
542 */
543 public function setConfirmed( bool $confirmed ): void {
544 $this->confirmed = $confirmed;
545 }
546
547 /**
548 * @internal Use withX() methods for new code.
549 */
550 public function setHash( string $hash ): void {
551 $this->hash = $hash;
552 }
553
554 /**
555 * @internal Use withX() methods for new code.
556 */
557 public function setUpdateTime( int $time ): void {
558 $this->updateTime = $time;
559 }
560
561 /**
562 * @internal Use withX() methods for new code.
563 */
564 public function setIpConfirmation( string $ip ): void {
565 $this->ipConfirmation = $ip;
566 }
567 }
568