PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.12.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.12.0
5.12.0 5.13.0 5.13.1 5.11.0 5.10.0 5.9.0 5.8.0 5.8.1 5.7.0 5.6.2 5.6.3 5.6.1 5.6.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 All 47 releases
double-opt-in / src / Entity / OptIn.php

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

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